Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 23 additions & 20 deletions apps/flutter_client_contract_test_service/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@ packages:
dependency: transitive
description:
name: characters
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
version: "1.4.0"
version: "1.4.1"
checked_yaml:
dependency: transitive
description:
Expand Down Expand Up @@ -388,33 +388,36 @@ packages:
source: hosted
version: "6.8.0"
launchdarkly_common_client:
dependency: "direct overridden"
dependency: transitive
Comment thread
abelonogov-ld marked this conversation as resolved.
description:
path: "../../packages/common_client"
relative: true
source: path
version: "1.9.0"
name: launchdarkly_common_client
sha256: aeefe7bc4b7bf995fa23a5165846a7320aa1eaa48089f19d150a0144fbf5bfad
url: "https://pub.dev"
source: hosted
version: "1.10.0"
launchdarkly_dart_common:
dependency: "direct overridden"
dependency: transitive
description:
path: "../../packages/common"
relative: true
source: path
version: "1.8.0"
name: launchdarkly_dart_common
sha256: e36fb8d943ea7a5b99e7ecdc361174ca95e05533904dd92175816ae5c9723eee
url: "https://pub.dev"
source: hosted
version: "1.8.1"
launchdarkly_event_source_client:
dependency: "direct overridden"
dependency: transitive
description:
path: "../../packages/event_source_client"
relative: true
source: path
version: "2.0.1"
name: launchdarkly_event_source_client
sha256: c248a81bc353d44f7f18803ff625d784d640265bf8bbc32c63f1d2d91911b88f
url: "https://pub.dev"
source: hosted
version: "2.1.0"
launchdarkly_flutter_client_sdk:
dependency: "direct main"
description:
path: "../../packages/flutter_client_sdk"
relative: true
source: path
version: "4.15.0"
version: "4.16.0"
lints:
dependency: "direct dev"
description:
Expand Down Expand Up @@ -451,10 +454,10 @@ packages:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.11.1"
version: "0.13.0"
meta:
dependency: transitive
description:
Expand Down
2 changes: 1 addition & 1 deletion apps/sse_contract_test_service/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ packages:
path: "../../packages/event_source_client"
relative: true
source: path
version: "2.0.1"
version: "2.1.0"
lints:
dependency: "direct dev"
description:
Expand Down
3 changes: 3 additions & 0 deletions packages/flutter_client_sdk/example/ios/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
**/dgph
# CocoaPods (regenerated by `flutter run`/`pod install`)
Podfile
Podfile.lock
*.mode1v3
*.mode2v3
*.moved-aside
Expand Down
4 changes: 4 additions & 0 deletions packages/flutter_client_sdk/example/macos/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
**/Flutter/ephemeral/
**/Pods/

# CocoaPods (regenerated by `flutter run`/`pod install`)
Podfile
Podfile.lock

# Xcode-related
**/dgph
**/xcuserdata/
37 changes: 29 additions & 8 deletions packages/flutter_client_sdk/lib/src/ld_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'connection_manager.dart';
import 'flutter_state_detector.dart';
import 'persistence/shared_preferences_persistence.dart';
import 'platform_env_reporter.dart';
import 'plugin.dart';

const sdkName = 'FlutterClientSdk';
const sdkVersion = '4.16.0'; // x-release-please-version
Expand Down Expand Up @@ -38,6 +39,7 @@ const sdkVersion = '4.16.0'; // x-release-please-version
interface class LDClient {
late final LDCommonClient _client;
late final ConnectionManager _connectionManager;
late final PluginEnvironmentMetadata _pluginEnvironmentMetadata;

/// Stream which emits data source status changes.
///
Expand Down Expand Up @@ -93,15 +95,14 @@ interface class LDClient {
final sdkPluginMetadata =
PluginSdkMetadata(name: sdkName, version: sdkVersion);

_pluginEnvironmentMetadata = PluginEnvironmentMetadata(
sdk: sdkPluginMetadata,
application: config.applicationInfo,
credential: PluginCredentialInfo(
type: _client.credentialType, value: config.sdkCredential));

safeRegisterPlugins(
this,
PluginEnvironmentMetadata(
sdk: sdkPluginMetadata,
application: config.applicationInfo,
credential: PluginCredentialInfo(
type: _client.credentialType, value: config.sdkCredential)),
config.plugins,
config.logger);
this, _pluginEnvironmentMetadata, config.plugins, config.logger);
}

/// Initialize the SDK.
Expand Down Expand Up @@ -358,4 +359,24 @@ interface class LDClient {
void addHook(Hook hook) {
_client.addHook(hook);
}

/// Registers a plugin with this SDK instance after the client has been
/// constructed.
///
/// Bundled hooks from the plugin are added before [Plugin.register] is
/// invoked. If reading [Plugin.hooks] throws, the plugin is not registered.
/// If [Plugin.register] throws, the error is logged and not rethrown.
void registerPlugin(Plugin plugin) {
final hooks = safeGetPluginHooks(plugin, _client.logger);
if (hooks == null) {
return;
}

for (final hook in hooks) {
addHook(hook);
}

safeRegisterPlugins(
this, _pluginEnvironmentMetadata, [plugin], _client.logger);
}
}
2 changes: 1 addition & 1 deletion packages/flutter_client_sdk/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies:
sdk: flutter
package_info_plus: ">=4.2.0 <11.0.0"
device_info_plus: ">=9.1.1 <14.0.0"
launchdarkly_common_client: 1.9.0
launchdarkly_common_client: 1.10.0
shared_preferences: ^2.2.2
connectivity_plus: ">=5.0.2 <8.0.0"
web: ^1.1.1
Expand Down
109 changes: 107 additions & 2 deletions packages/flutter_client_sdk/test/ld_client_plugin_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,27 @@ final class TestPlugin extends Plugin {
final String pluginName;
final List<Hook> _hooks;
final bool shouldThrowOnRegister;
final bool shouldThrowOnGetHooks;
final PluginMetadata _metadata;

int registerCallCount = 0;
LDClient? lastClientReceived;
PluginEnvironmentMetadata? lastEnvironmentMetadataReceived;

TestPlugin(this.pluginName, this._hooks, {this.shouldThrowOnRegister = false})
TestPlugin(this.pluginName, this._hooks,
{this.shouldThrowOnRegister = false, this.shouldThrowOnGetHooks = false})
: _metadata = PluginMetadata(name: pluginName);

@override
PluginMetadata get metadata => _metadata;

@override
List<Hook> get hooks => _hooks;
List<Hook> get hooks {
if (shouldThrowOnGetHooks) {
throw Exception('Test exception from plugin $pluginName');
}
return _hooks;
}

@override
void register(
Expand All @@ -99,6 +106,29 @@ final class TestPlugin extends Plugin {
}
}

final class EvaluationOnRegisterPlugin extends Plugin {
final TestHook hook;
final PluginMetadata _metadata;

int registerCallCount = 0;

EvaluationOnRegisterPlugin(this.hook)
: _metadata = PluginMetadata(name: 'evaluation-on-register-plugin');

@override
PluginMetadata get metadata => _metadata;

@override
List<Hook> get hooks => [hook];

@override
void register(
LDClient client, PluginEnvironmentMetadata environmentMetadata) {
registerCallCount++;
client.boolVariation('test-flag', false);
}
}

final class _WifiConnected extends ConnectivityPlatform {
final StreamController<List<ConnectivityResult>> _controller =
StreamController();
Expand Down Expand Up @@ -393,4 +423,79 @@ void main() {
client.close();
});
});

group('LDClient registerPlugin', () {
test('registers plugin with environment metadata', () {
final plugin = TestPlugin('runtime-plugin', []);
final client = createTestClient();

client.registerPlugin(plugin);

expect(plugin.registerCallCount, equals(1));
expect(plugin.lastClientReceived, same(client));
expect(plugin.lastEnvironmentMetadataReceived, isNotNull);
expect(plugin.lastEnvironmentMetadataReceived!.sdk.name,
equals('FlutterClientSdk'));
expect(plugin.lastEnvironmentMetadataReceived!.credential.value,
equals('test-mobile-key'));

client.close();
});

test('activates bundled hooks after registerPlugin', () {
final hook = TestHook('runtime-plugin-hook');
final plugin = TestPlugin('runtime-plugin', [hook]);
final client = createTestClient();

client.registerPlugin(plugin);
hook.callLog.clear();
client.boolVariation('test-flag', false);

expect(hook.callLog.any((call) => call.startsWith('beforeEvaluation')),
isTrue);
expect(hook.callLog.any((call) => call.startsWith('afterEvaluation')),
isTrue);

client.close();
});

test('adds hooks before plugin register is invoked', () {
final hook = TestHook('runtime-order-hook');
final plugin = EvaluationOnRegisterPlugin(hook);
final client = createTestClient();

client.registerPlugin(plugin);

expect(plugin.registerCallCount, equals(1));
expect(hook.callLog.any((call) => call.startsWith('beforeEvaluation')),
isTrue);
expect(hook.callLog.any((call) => call.startsWith('afterEvaluation')),
isTrue);

client.close();
});

test('does not register plugin when hooks getter throws', () {
final plugin =
TestPlugin('bad-hooks-plugin', [], shouldThrowOnGetHooks: true);
final client = createTestClient();

client.registerPlugin(plugin);

expect(plugin.registerCallCount, equals(0));

client.close();
});

test('does not throw when plugin register throws', () {
final plugin =
TestPlugin('bad-register-plugin', [], shouldThrowOnRegister: true);
final client = createTestClient();

expect(() => client.registerPlugin(plugin), returnsNormally);
expect(plugin.registerCallCount, equals(1));

client.close();
});
});
}
Loading