Skip to content
Merged
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
17 changes: 17 additions & 0 deletions pkgs/shared/common/src/CredentialType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace LaunchDarkly.Sdk
{
/// <summary>
/// The type of credential used for the environment.
/// </summary>
public enum CredentialType
{
/// <summary>
/// A mobile key credential.
/// </summary>
MobileKey,
/// <summary>
/// An SDK key credential.
/// </summary>
SdkKey
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace LaunchDarkly.Sdk.Integrations.Plugins
{
/// <summary>
/// Metadata about the application where the SDK is running.
/// </summary>
public sealed class ApplicationMetadata
{
/// <summary>
/// Gets the application identifier.
/// </summary>
public string Id { get; }

/// <summary>
/// Gets the application version.
/// </summary>
public string Version { get; }

/// <summary>
/// Gets the application name.
/// </summary>
public string Name { get; }

/// <summary>
/// Gets the application version name.
/// </summary>
public string VersionName { get; }

/// <summary>
/// Initializes a new instance of the <see cref="ApplicationMetadata"/> class with the specified application ID, version, name, and version name.
/// </summary>
/// <param name="id">The application identifier.</param>
/// <param name="version">The application version.</param>
/// <param name="name">The application name.</param>
/// <param name="versionName">The application version name.</param>
public ApplicationMetadata(string id = null, string version = null, string name = null, string versionName = null)
{
Id = id;
Version = version;
Name = name;
VersionName = versionName;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace LaunchDarkly.Sdk.Integrations.Plugins
{
/// <summary>
/// Metadata about the environment where the SDK is running.
/// </summary>
public sealed class EnvironmentMetadata
{
/// <summary>
/// Gets the SDK metadata.
/// </summary>
public SdkMetadata Sdk { get; }

/// <summary>
/// Gets the SDK key.
/// </summary>
public string Credential { get; }

/// <summary>
/// Gets the type of credential used (e.g., Mobile Key or SDK Key).
/// </summary>
public CredentialType CredentialType { get; }

/// <summary>
/// Gets the application metadata.
/// </summary>
public ApplicationMetadata Application { get; }

/// <summary>
/// Initializes a new instance of the <see cref="EnvironmentMetadata"/> class.
/// </summary>
/// <param name="sdkMetadata">the SDK metadata</param>
/// <param name="credential">the SDK Key or Mobile Key</param>
/// <param name="credentialType">the type of credential</param>
/// <param name="applicationMetadata">the application metadata</param>
public EnvironmentMetadata(SdkMetadata sdkMetadata, string credential, CredentialType credentialType, ApplicationMetadata applicationMetadata)
{
Sdk = sdkMetadata;
Credential = credential;
CredentialType = credentialType;
Application = applicationMetadata;
}
}
}
50 changes: 50 additions & 0 deletions pkgs/shared/common/src/Integrations/Plugins/PluginBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System.Collections.Generic;

namespace LaunchDarkly.Sdk.Integrations.Plugins
{
/// <summary>
/// Abstract base class for extending SDK functionality via plugins.
/// Consumers should provide specific implementations of this class with the appropariate
/// client and hook types for their use case.
/// This class includes default implementations for optional methods, allowing
/// LaunchDarkly to expand the list of plugin methods without breaking customer integrations.
/// Plugins provide an interface which allows for initialization, access to credentials,
/// and hook registration in a single interface.
/// </summary>
/// <typeparam name="TClient">The type of the LaunchDarkly client (e.g., ILdClient)</typeparam>
/// <typeparam name="THook">The type of hooks used by this plugin (e.g., Hook)</typeparam>
public abstract class PluginBase<TClient, THook>
{
/// <summary>
/// Get metadata about the plugin implementation.
/// </summary>
/// <returns>Metadata describing this plugin</returns>
public PluginMetadata Metadata { get; private set; }

/// <summary>
/// Initializes a new instance of the <see cref="PluginBase{TClient, THook}"/> class with the specified name.
/// </summary>
/// <param name="name">The name of the plugin.</param>
public PluginBase(string name)
{
Metadata = new PluginMetadata(name);
}

/// <summary>
/// Registers the plugin with the specified LaunchDarkly client and environment metadata.
/// </summary>
/// <param name="client">An instance of the LaunchDarkly client to register the plugin with.</param>
/// <param name="metadata">Metadata about the environment.</param>
public abstract void Register(TClient client, EnvironmentMetadata metadata);

/// <summary>
/// Returns a list of hooks to be registered for the plugin, based on the provided environment metadata.
/// </summary>
/// <param name="metadata">Metadata about the environment.</param>
/// <returns>A list of hook instances to be registered.</returns>
public virtual IList<THook> GetHooks(EnvironmentMetadata metadata)
{
return new List<THook>();
}
}
}
88 changes: 88 additions & 0 deletions pkgs/shared/common/src/Integrations/Plugins/PluginExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Linq;
using LaunchDarkly.Logging;

namespace LaunchDarkly.Sdk.Integrations.Plugins
{
/// <summary>
/// Extension methods for plugin registration and hook collection.
/// </summary>
public static class PluginExtensions
{
/// <summary>
/// Registers all plugins with the client and environment metadata.
/// </summary>
/// <typeparam name="TClient">The client type (e.g., ILdClient)</typeparam>
/// <typeparam name="THook">The hook type (e.g., Hook)</typeparam>
/// <param name="client">The client instance to register plugins with</param>
/// <param name="plugins">The collection of plugins to register</param>
/// <param name="environmentMetadata">Metadata about the environment</param>
/// <param name="logger">Logger for error reporting</param>
/// <remarks>
/// This method iterates through each plugin in the collection and calls its `Register` method
/// to initialize it with the client and environment metadata. It logs any exceptions that occur during
/// the registration process, allowing the client to continue functioning even if some plugins fail to register.
/// </remarks>
public static void RegisterPlugins<TClient, THook>(
this TClient client,
IEnumerable<PluginBase<TClient, THook>> plugins,
EnvironmentMetadata environmentMetadata,
Logger logger)
{
foreach (var plugin in plugins)
{
try
{
plugin.Register(client, environmentMetadata);
}
catch (Exception ex)
{
logger.Error("Error registering plugin {0}: {1}",
plugin.Metadata.Name ?? "unknown", ex);
}
}
}

/// <summary>
/// Retrieves all hooks from the specified plugins.
/// </summary>
/// <typeparam name="TClient">The client type</typeparam>
/// <typeparam name="THook">The hook type</typeparam>
/// <param name="client">The client instance to register plugins with</param>
/// <param name="plugins">The collection of plugins</param>
/// <param name="environmentMetadata">Metadata about the environment</param>
/// <param name="logger">Logger for error reporting</param>
/// <returns>A list of hooks from all plugins</returns>
/// <remarks>
/// This method iterates through each plugin in the collection and calls its `GetHooks` method
/// to retrieve any hooks the plugin provides. It logs any exceptions that occur during
/// the hook retrieval process and continues processing remaining plugins.
/// </remarks>
public static List<THook> GetPluginHooks<TClient, THook>(
this TClient client,
IEnumerable<PluginBase<TClient, THook>> plugins,
EnvironmentMetadata environmentMetadata,
Logger logger)
{
var allHooks = new List<THook>();
foreach (var plugin in plugins)
{
try
{
var pluginHooks = plugin.GetHooks(environmentMetadata);
if (pluginHooks != null)
{
allHooks.AddRange(pluginHooks);
}
}
catch (Exception ex)
{
logger.Error("Error getting hooks from plugin {0}: {1}",
plugin.Metadata.Name ?? "unknown", ex);
}
}
return allHooks;
}
}
}
24 changes: 24 additions & 0 deletions pkgs/shared/common/src/Integrations/Plugins/PluginMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;

namespace LaunchDarkly.Sdk.Integrations.Plugins
{
/// <summary>
/// Metadata about a plugin implementation.
/// </summary>
public sealed class PluginMetadata
{
/// <summary>
/// The name of the plugin.
/// </summary>
public string Name { get; }

/// <summary>
/// Initializes a new instance of the <see cref="PluginMetadata"/> class with the specified name.
/// </summary>
/// <param name="name">The name of the plugin.</param>
public PluginMetadata(string name)
{
Name = name;
}
}
}
44 changes: 44 additions & 0 deletions pkgs/shared/common/src/Integrations/Plugins/SdkMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace LaunchDarkly.Sdk.Integrations.Plugins
{
/// <summary>
/// Metadata about the SDK itself.
/// </summary>
public sealed class SdkMetadata
{
/// <summary>
/// Gets the id of the SDK. This should match the identifier in the SDK.
/// This field should be either the x-launchdarkly-user-agent or the user-agent.
/// </summary>
public string Name { get; }

/// <summary>
/// Gets the version of the SDK.
/// </summary>
public string Version { get; }

/// <summary>
/// Gets the wrapper name if this SDK is a wrapper.
/// </summary>
public string WrapperName { get; }

/// <summary>
/// Gets the wrapper version if this SDK is a wrapper.
/// </summary>
public string WrapperVersion { get; }

/// <summary>
/// Initializes a new instance of the <see cref="SdkMetadata"/> class.
/// </summary>
/// <param name="name">The id of the SDK. This should match the identifier in the SDK. It should be either the x-launchdarkly-user-agent or the user-agent.</param>
/// <param name="version">The version of the SDK.</param>
/// <param name="wrapperName">If this SDK is a wrapper, then this should be the wrapper name.</param>
/// <param name="wrapperVersion">If this SDK is a wrapper, then this should be the wrapper version.</param>
public SdkMetadata(string name, string version, string wrapperName = null, string wrapperVersion = null)
{
Name = name;
Version = version;
WrapperName = wrapperName;
WrapperVersion = wrapperVersion;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Xunit;

namespace LaunchDarkly.Sdk.Integrations.Plugins
{
public class ApplicationMetadataTest
{
[Fact]
public void CanConstructWithAllParameters()
{
var appMetadata = new ApplicationMetadata("app-id", "1.0.0", "My App", "v1.0.0");

Assert.Equal("app-id", appMetadata.Id);
Assert.Equal("1.0.0", appMetadata.Version);
Assert.Equal("My App", appMetadata.Name);
Assert.Equal("v1.0.0", appMetadata.VersionName);
}

[Fact]
public void CanConstructWithDefaultParameters()
{
var appMetadata = new ApplicationMetadata();

Assert.Null(appMetadata.Id);
Assert.Null(appMetadata.Version);
Assert.Null(appMetadata.Name);
Assert.Null(appMetadata.VersionName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Xunit;

namespace LaunchDarkly.Sdk.Integrations.Plugins
{
public class EnvironmentMetadataTest
{
[Fact]
public void CanConstructWithAllParameters()
{
var sdkMetadata = new SdkMetadata("dotnet-server-sdk", "6.0.0");
var appMetadata = new ApplicationMetadata("app-id", "1.0.0");
var envMetadata = new EnvironmentMetadata(sdkMetadata, "test-sdk-key", CredentialType.SdkKey, appMetadata);

Assert.Equal(sdkMetadata, envMetadata.Sdk);
Assert.Equal("test-sdk-key", envMetadata.Credential);
Assert.Equal(CredentialType.SdkKey, envMetadata.CredentialType);
Assert.Equal(appMetadata, envMetadata.Application);
}
}
}
Loading
Loading