-
Notifications
You must be signed in to change notification settings - Fork 19
feat: Add experimental plugin support. #225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
727ac25
to
ade623a
Compare
/// Implementation note: SDK packages must export a specialized version of this | ||
/// for their specific TClient type. This class cannot provide a type, because | ||
/// it would limit the API to methods available in the base client. | ||
abstract base class PluginBase<TClient> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generic base class that is then specialized in the leaf-node SDK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exposed the credential type here on the common-client. It isn't exposed on the SDK client, but exposing it would probably be fine if we choose to in the future.
/// @override | ||
/// PluginMetadata get metadata => _metadata; | ||
/// ``` | ||
PluginMetadata get metadata; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No body for the getter makes this abstract.
import 'ld_client.dart'; | ||
|
||
/// Base class from which all plugins must derive. | ||
abstract base class Plugin extends PluginBase<LDClient> {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what finally gets exported from the SDK package and makes the plugins non-generic.
import 'hook.dart'; | ||
|
||
List<Hook>? combineHooks(List<Hook>? baseHooks, List<Hook>? extendedHooks) { | ||
if (baseHooks == null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If both are null, that is fine as well, we just end up returning null, which the calling code will then pass to an optional constructor argument.
|
||
export 'src/plugins/operations.dart' show safeGetHooks, safeRegisterPlugins; | ||
|
||
export 'src/config/defaults/credential_type.dart' show CredentialType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is exported now so that it can be a component of the meta-data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file isn't strictly part of the plugins support. We have not released hooks yet and this should be a readonly list.
allAttributesPrivate ?? DefaultConfig.allAttributesPrivate, | ||
globalPrivateAttributes = globalPrivateAttributes ?? []; | ||
globalPrivateAttributes = globalPrivateAttributes ?? [], | ||
hooks = hooks != null ? UnmodifiableListView(List.from(hooks)) : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make an unmodifiable list from a copy of the input list. If a copy isn't made, then the original list could be mutated and the view would reflect those mutations.
} | ||
} | ||
|
||
final class _WifiConnected extends ConnectivityPlatform { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally speaking we do almost all testing at the dart level instead of the flutter level, but the plugins need logic at the flutter level, so we need to do testing in flutter.
When running flutter unit tests you have to mock the rest of the environment dependencies. We depend on the widget tree, connectivity, and persistent storage. So all of those need to be mocked. If we add more flutter level tests, then we should probably move these to some common test file.
final LDContext testContext = | ||
LDContextBuilder().kind('user', 'test-user-key').build(); | ||
|
||
final config = LDConfig( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As inert a client as possible.
🤖 I have created a release *beep* *boop* --- ## [1.7.0](launchdarkly_common_client-v1.6.2...launchdarkly_common_client-v1.7.0) (2025-09-12) ### Features * Add experimental plugin support. ([#225](#225)) ([5bd9ce7](5bd9ce7)) * Add support for hooks. ([#220](#220)) ([6e7a26d](6e7a26d)) * Internal environment ID support. ([#217](#217)) ([71b522b](71b522b)) ### Bug Fixes * Change hook data values to `dynamic` from `LDValue`. ([d7720f3](d7720f3)) * Export required plugin meta-data types. ([d7720f3](d7720f3)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [4.12.0](4.11.2...4.12.0) (2025-09-15) ### Features * Add experimental plugin support. ([#225](#225)) ([5bd9ce7](5bd9ce7)) * Add hook support and experimental plugin support. ([#228](#228)) ([b698905](b698905)) * Add support for hooks. ([#220](#220)) ([6e7a26d](6e7a26d)) * Update version constraints for device_info_plus to allow using 12.x. ([9b5f530](9b5f530)) * Update version constraints for package_info_plus to allow using 9.x. ([9b5f530](9b5f530)) ### Bug Fixes * Change hook data values to `dynamic` from `LDValue`. ([d7720f3](d7720f3)) * Export required plugin meta-data types. ([d7720f3](d7720f3)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: Ryan Lamb <[email protected]>
The flutter SDK is arranged into a common-client package that is independent of the flutter package to allow for eventually supporting non-flutter usages such as dart command line applications. This means, similar to JS, that plugins are a concern of the leaf-node of the SDK because plugins depend on types that are not common to the leaf nodes.
A plugin for the flutter client SDK is registered with an instance of the flutter client SDK and not an instance of the common client SDK. (In flutter the common client is actually composed into the leaf-node and not a base class, so this difference is even more meaningful.)
The work-around to this is to make common types for all the meta-data, and then a generic base class to be extended by the leaf-node implementations. That extended version makes the client concrete instead of generic. The base operations that need to be done on plugins, such as getting a list of hooks from them, and registering them, are handled via generic methods in the common client package.