-
Notifications
You must be signed in to change notification settings - Fork 8
feat: Add experimental plugin support #139
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
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
104c828
feat: Add experimental plugin support
devin-ai-integration[bot] 1df96bf
fix: Address GitHub PR comments
devin-ai-integration[bot] 178c37d
docs: Complete XML documentation for PluginConfigurationBuilder and E…
devin-ai-integration[bot] 9556177
clean up logic and add missing docs
jsonbailey 0189e59
fix: Unify config vs _configuration usage and fix WrapperInfo propert…
devin-ai-integration[bot] 4d4720e
docs: Add missing XML documentation summaries for plugin classes
devin-ai-integration[bot] 07731b2
remove devins duplicate method
jsonbailey 464ae28
move plugin classes into common package
jsonbailey bdf475b
adding testing and fixes
jsonbailey 8b71069
fix style issues
jsonbailey 569ad87
Merge branch 'main' into devin/sdk-1324/experimental-plugin-support
jsonbailey 3a6b0ae
add latest common and internal for package support
jsonbailey a20df4d
remove whitespace
jsonbailey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
pkgs/sdk/server/src/Integrations/PluginConfigurationBuilder.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using LaunchDarkly.Sdk.Server.Plugins; | ||
| using LaunchDarkly.Sdk.Server.Subsystems; | ||
|
|
||
| namespace LaunchDarkly.Sdk.Server.Integrations | ||
| { | ||
| /// <summary> | ||
| /// PluginConfigurationBuilder is a builder for the SDK's plugin configuration. | ||
| /// </summary> | ||
| public sealed class PluginConfigurationBuilder | ||
| { | ||
| private readonly List<Plugin> _plugins; | ||
|
|
||
| /// <summary> | ||
| /// Constructs a configuration from an existing collection of plugins. | ||
| /// </summary> | ||
| public PluginConfigurationBuilder(IEnumerable<Plugin> plugins = null) | ||
| { | ||
| _plugins = plugins is null ? new List<Plugin>() : plugins.ToList(); | ||
| } | ||
|
|
||
| /// <summary> | ||
jsonbailey marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// Adds a plugin to the configuration. | ||
| /// </summary> | ||
| /// <returns>the builder</returns> | ||
| public PluginConfigurationBuilder Add(Plugin plugin) | ||
| { | ||
| _plugins.Add(plugin); | ||
| return this; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Builds the configuration. | ||
| /// </summary> | ||
| /// <returns>the built configuration</returns> | ||
| public PluginConfiguration Build() | ||
| { | ||
| return new PluginConfiguration(_plugins.ToList()); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ | |
| using LaunchDarkly.Sdk.Server.Hooks; | ||
| using LaunchDarkly.Sdk.Server.Interfaces; | ||
| using LaunchDarkly.Sdk.Server.Internal; | ||
| using LaunchDarkly.Sdk.Integrations.Plugins; | ||
| using LaunchDarkly.Sdk.Server.Internal.BigSegments; | ||
| using LaunchDarkly.Sdk.Server.Internal.DataSources; | ||
| using LaunchDarkly.Sdk.Server.Internal.DataStores; | ||
|
|
@@ -130,7 +131,7 @@ public LdClient(Configuration config) | |
| { | ||
| _configuration = config; | ||
|
|
||
| var logConfig = (config.Logging ?? Components.Logging()).Build(new LdClientContext(config.SdkKey)); | ||
| var logConfig = (_configuration.Logging ?? Components.Logging()).Build(new LdClientContext(_configuration.SdkKey)); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use of |
||
|
|
||
| _log = logConfig.LogAdapter.Logger(logConfig.BaseLoggerName ?? LogNames.DefaultBase); | ||
| _log.Info("Starting LaunchDarkly client {0}", | ||
|
|
@@ -140,24 +141,24 @@ public LdClient(Configuration config) | |
| var taskExecutor = new TaskExecutor(this, _log); | ||
|
|
||
| var clientContext = new LdClientContext( | ||
| config.SdkKey, | ||
| _configuration.SdkKey, | ||
| null, | ||
| null, | ||
| null, | ||
| _log, | ||
| config.Offline, | ||
| config.ServiceEndpoints, | ||
| _configuration.Offline, | ||
| _configuration.ServiceEndpoints, | ||
| null, | ||
| taskExecutor, | ||
| config.ApplicationInfo?.Build() ?? new ApplicationInfo(), | ||
| config.WrapperInfo?.Build() | ||
| _configuration.ApplicationInfo?.Build() ?? new ApplicationInfo(), | ||
| _configuration.WrapperInfo?.Build() | ||
| ); | ||
|
|
||
| var httpConfig = (config.Http ?? Components.HttpConfiguration()).Build(clientContext); | ||
| var httpConfig = (_configuration.Http ?? Components.HttpConfiguration()).Build(clientContext); | ||
| clientContext = clientContext.WithHttp(httpConfig); | ||
|
|
||
| var diagnosticStore = _configuration.DiagnosticOptOut ? null : | ||
| new ServerDiagnosticStore(config, clientContext); | ||
| new ServerDiagnosticStore(_configuration, clientContext); | ||
| clientContext = clientContext.WithDiagnosticStore(diagnosticStore); | ||
|
|
||
| var dataStoreUpdates = new DataStoreUpdatesImpl(taskExecutor, _log.SubLogger(LogNames.DataStoreSubLog)); | ||
|
|
@@ -185,25 +186,33 @@ public LdClient(Configuration config) | |
| ); | ||
|
|
||
| var eventProcessorFactory = | ||
| config.Offline ? Components.NoEvents : | ||
| (_configuration.Events?? Components.SendEvents()); | ||
| _configuration.Offline ? Components.NoEvents : | ||
| (_configuration.Events ?? Components.SendEvents()); | ||
| _eventProcessor = eventProcessorFactory.Build(clientContext); | ||
|
|
||
| var dataSourceUpdates = new DataSourceUpdatesImpl(_dataStore, _dataStoreStatusProvider, | ||
| taskExecutor, _log, logConfig.LogDataSourceOutageAsErrorAfter); | ||
| IComponentConfigurer<IDataSource> dataSourceFactory = | ||
| config.Offline ? Components.ExternalUpdatesOnly : | ||
| _configuration.Offline ? Components.ExternalUpdatesOnly : | ||
| (_configuration.DataSource ?? Components.StreamingDataSource()); | ||
| _dataSource = dataSourceFactory.Build(clientContext.WithDataSourceUpdates(dataSourceUpdates)); | ||
| _dataSourceStatusProvider = new DataSourceStatusProviderImpl(dataSourceUpdates); | ||
| _flagTracker = new FlagTrackerImpl(dataSourceUpdates, | ||
| (string key, Context context) => JsonVariation(key, context, LdValue.Null)); | ||
|
|
||
| var hookConfig = (config.Hooks ?? Components.Hooks()).Build(); | ||
| _hookExecutor = hookConfig.Hooks.Any() ? | ||
| (IHookExecutor) new Executor(_log.SubLogger(LogNames.HooksSubLog), hookConfig.Hooks) | ||
| var allHooks = new List<Hook>(); | ||
|
|
||
| var hookConfig = (_configuration.Hooks ?? Components.Hooks()).Build(); | ||
| allHooks.AddRange(hookConfig.Hooks); | ||
|
|
||
| var pluginConfig = (_configuration.Plugins ?? Components.Plugins()).Build(); | ||
| EnvironmentMetadata environmentMetadata = CreateEnvironmentMetadata(clientContext); | ||
| allHooks.AddRange(this.GetPluginHooks(pluginConfig.Plugins, environmentMetadata, _log)); | ||
| _hookExecutor = allHooks.Any() ? | ||
| (IHookExecutor)new Executor(_log.SubLogger(LogNames.HooksSubLog), allHooks) | ||
| : new NoopExecutor(); | ||
|
|
||
| this.RegisterPlugins(pluginConfig.Plugins, environmentMetadata, _log); | ||
|
|
||
| var initTask = _dataSource.Start(); | ||
|
|
||
|
|
@@ -323,7 +332,7 @@ public MigrationVariation MigrationVariation(string key, Context context, Migrat | |
| var (detail, flag) = EvaluateWithHooks(Method.MigrationVariation, key, context, LdValue.Of(defaultStage.ToDataModelString()), | ||
| LdValue.Convert.String, true, EventFactory.Default); | ||
|
|
||
| var nullableStage = MigrationStageExtensions.FromDataModelString(detail.Value); | ||
| var nullableStage = MigrationStageExtensions.FromDataModelString(detail.Value); | ||
| var stage = nullableStage ?? defaultStage; | ||
| if (nullableStage == null) | ||
| { | ||
|
|
@@ -648,24 +657,34 @@ public Logger GetLogger() | |
|
|
||
| #region Private methods | ||
|
|
||
| private FeatureFlag GetFlag(string key) | ||
| /// <summary> | ||
| /// Creates metadata about the environment, including SDK and application information. | ||
| /// </summary> | ||
| /// <param name="clientContext">The client context containing application and wrapper information.</param> | ||
| /// <returns>An <see cref="EnvironmentMetadata"/> instance containing the environment metadata.</returns> | ||
| /// <remarks> | ||
| /// This method constructs the environment metadata using the SDK key, application ID, and version, | ||
| /// along with any wrapper information if available. It is used to provide context for plugins and | ||
| /// hooks that may need to interact with the environment. | ||
| /// </remarks> | ||
| private EnvironmentMetadata CreateEnvironmentMetadata(LdClientContext clientContext) | ||
| { | ||
| var maybeItem = _dataStore.Get(DataModel.Features, key); | ||
| if (maybeItem.HasValue && maybeItem.Value.Item != null && maybeItem.Value.Item is FeatureFlag f) | ||
| { | ||
| return f; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| private Segment GetSegment(string key) | ||
| { | ||
| var maybeItem = _dataStore.Get(DataModel.Segments, key); | ||
| if (maybeItem.HasValue && maybeItem.Value.Item != null && maybeItem.Value.Item is Segment s) | ||
| { | ||
| return s; | ||
| } | ||
| return null; | ||
| var applicationInfo = clientContext.ApplicationInfo; | ||
| var wrapperInfo = _configuration.WrapperInfo?.Build(); | ||
|
|
||
| var sdkMetadata = new SdkMetadata( | ||
| "dotnet-server-sdk", | ||
| AssemblyVersions.GetAssemblyVersionStringForType(typeof(LdClient)), | ||
| wrapperInfo?.Name, | ||
| wrapperInfo?.Version | ||
| ); | ||
|
|
||
| var applicationMetadata = new ApplicationMetadata( | ||
| applicationInfo.ApplicationId, | ||
| applicationInfo.ApplicationVersion | ||
| ); | ||
|
|
||
| return new EnvironmentMetadata(sdkMetadata, _configuration.SdkKey, CredentialType.SdkKey, applicationMetadata); | ||
| } | ||
|
|
||
| private void Dispose(bool disposing) | ||
|
|
@@ -694,6 +713,26 @@ private string GetEnvironmentId() | |
| return null; | ||
| } | ||
|
|
||
| private FeatureFlag GetFlag(string key) | ||
| { | ||
| var maybeItem = _dataStore.Get(DataModel.Features, key); | ||
| if (maybeItem.HasValue && maybeItem.Value.Item != null && maybeItem.Value.Item is FeatureFlag f) | ||
| { | ||
| return f; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| private Segment GetSegment(string key) | ||
| { | ||
| var maybeItem = _dataStore.Get(DataModel.Segments, key); | ||
| if (maybeItem.HasValue && maybeItem.Value.Item != null && maybeItem.Value.Item is Segment s) | ||
| { | ||
| return s; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| #endregion | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| using System.Collections.Generic; | ||
| using LaunchDarkly.Sdk.Server.Hooks; | ||
| using LaunchDarkly.Sdk.Server.Interfaces; | ||
| using LaunchDarkly.Sdk.Integrations.Plugins; | ||
|
|
||
| namespace LaunchDarkly.Sdk.Server.Plugins | ||
| { | ||
| /// <summary> | ||
| /// Abstract base class for extending SDK functionality via plugins in the server-side SDK. | ||
| /// All provided server-side plugin implementations MUST inherit from this class. | ||
| /// </summary> | ||
| public abstract class Plugin : PluginBase<ILdClient, Hook> | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="Plugin"/> class with the specified name. | ||
| /// </summary> | ||
| /// <param name="name">The name of the plugin.</param> | ||
| protected Plugin(string name) | ||
| : base(name) | ||
| { | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| using System.Collections.Generic; | ||
| using LaunchDarkly.Sdk.Server.Plugins; | ||
|
|
||
| namespace LaunchDarkly.Sdk.Server.Subsystems | ||
| { | ||
| /// <summary> | ||
| /// Configuration containing plugins for the SDK. | ||
| /// </summary> | ||
| public sealed class PluginConfiguration | ||
| { | ||
| /// <summary> | ||
| /// The collection of plugins. | ||
| /// </summary> | ||
| public IEnumerable<Plugin> Plugins { get; } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="PluginConfiguration"/> class with the specified plugins. | ||
| /// </summary> | ||
| /// <param name="plugins">The plugins to include in this configuration.</param> | ||
| public PluginConfiguration(IEnumerable<Plugin> plugins) | ||
| { | ||
| Plugins = plugins; | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.