Add flagsmith
This commit is contained in:
@@ -651,7 +651,6 @@ public class BacktestController : BaseController
|
||||
{
|
||||
var user = await GetUser();
|
||||
|
||||
|
||||
if (string.IsNullOrEmpty(request.UniversalConfig.ScenarioName) && request.UniversalConfig.Scenario == null)
|
||||
{
|
||||
return BadRequest("Either scenario name or scenario object is required in universal configuration");
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
"DebitEndpoint": "/api/credits/debit",
|
||||
"RefundEndpoint": "/api/credits/refund"
|
||||
},
|
||||
"Flagsmith": {
|
||||
"ApiKey": "ser.ShJJJMtWYS9fwuzd83ejwR",
|
||||
"ApiUrl": "https://flag.kaigen.ai/api/v1/"
|
||||
},
|
||||
"N8n": {
|
||||
"WebhookUrl": "https://n8n.kai.managing.live/webhook/fa9308b6-983b-42ec-b085-71599d655951",
|
||||
"IndicatorRequestWebhookUrl": "https://n8n.kai.managing.live/webhook/3aa07b66-1e64-46a7-8618-af300914cb11",
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
namespace Managing.Application.Abstractions.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Interface for Flagsmith feature flag service
|
||||
/// </summary>
|
||||
public interface IFlagsmithService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets flags for a specific user identity
|
||||
/// </summary>
|
||||
/// <param name="identity">The user identity identifier</param>
|
||||
/// <returns>Flags object for the identity</returns>
|
||||
Task<IFlagsmithFlags> GetIdentityFlagsAsync(string identity);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a feature is enabled for a specific identity
|
||||
/// </summary>
|
||||
/// <param name="identity">The user identity identifier</param>
|
||||
/// <param name="featureName">The name of the feature flag</param>
|
||||
/// <returns>True if the feature is enabled</returns>
|
||||
Task<bool> IsFeatureEnabledAsync(string identity, string featureName);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the feature value for a specific identity
|
||||
/// </summary>
|
||||
/// <param name="identity">The user identity identifier</param>
|
||||
/// <param name="featureName">The name of the feature flag</param>
|
||||
/// <returns>The feature value as string</returns>
|
||||
Task<string?> GetFeatureValueAsync(string identity, string featureName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper interface for Flagsmith flags to enable testing
|
||||
/// </summary>
|
||||
public interface IFlagsmithFlags
|
||||
{
|
||||
Task<bool> IsFeatureEnabled(string featureName);
|
||||
Task<string?> GetFeatureValue(string featureName);
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="FluentValidation" Version="11.9.1"/>
|
||||
<PackageReference Include="Flagsmith" Version="5.0.0"/>
|
||||
<PackageReference Include="GeneticSharp" Version="3.1.4"/>
|
||||
<PackageReference Include="MediatR" Version="12.2.0"/>
|
||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.1.0"/>
|
||||
|
||||
122
src/Managing.Application/Shared/FlagsmithService.cs
Normal file
122
src/Managing.Application/Shared/FlagsmithService.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using Flagsmith;
|
||||
using Managing.Application.Abstractions.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Managing.Application.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// Configuration settings for Flagsmith
|
||||
/// </summary>
|
||||
public class FlagsmithSettings
|
||||
{
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// API URL for self-hosted Flagsmith instance (required).
|
||||
/// </summary>
|
||||
public string ApiUrl { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service for managing feature flags using Flagsmith
|
||||
/// </summary>
|
||||
public class FlagsmithService : IFlagsmithService
|
||||
{
|
||||
private readonly FlagsmithClient _flagsmithClient;
|
||||
private readonly ILogger<FlagsmithService> _logger;
|
||||
|
||||
public FlagsmithService(FlagsmithClient flagsmithClient, ILogger<FlagsmithService> logger)
|
||||
{
|
||||
_flagsmithClient = flagsmithClient ?? throw new ArgumentNullException(nameof(flagsmithClient));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public async Task<IFlagsmithFlags> GetIdentityFlagsAsync(string identity)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(identity))
|
||||
{
|
||||
throw new ArgumentException("Identity cannot be null or empty", nameof(identity));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var flags = await _flagsmithClient.GetIdentityFlags(identity);
|
||||
return new FlagsmithFlagsWrapper(flags);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting flags for identity {Identity}", identity);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> IsFeatureEnabledAsync(string identity, string featureName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(identity))
|
||||
{
|
||||
throw new ArgumentException("Identity cannot be null or empty", nameof(identity));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(featureName))
|
||||
{
|
||||
throw new ArgumentException("Feature name cannot be null or empty", nameof(featureName));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var flags = await GetIdentityFlagsAsync(identity);
|
||||
return await flags.IsFeatureEnabled(featureName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error checking feature {FeatureName} for identity {Identity}", featureName, identity);
|
||||
return false; // Default to false on error
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string?> GetFeatureValueAsync(string identity, string featureName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(identity))
|
||||
{
|
||||
throw new ArgumentException("Identity cannot be null or empty", nameof(identity));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(featureName))
|
||||
{
|
||||
throw new ArgumentException("Feature name cannot be null or empty", nameof(featureName));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var flags = await GetIdentityFlagsAsync(identity);
|
||||
return await flags.GetFeatureValue(featureName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting feature value {FeatureName} for identity {Identity}", featureName, identity);
|
||||
return null; // Default to null on error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for Flagsmith flags to implement IFlagsmithFlags interface
|
||||
/// </summary>
|
||||
internal class FlagsmithFlagsWrapper : IFlagsmithFlags
|
||||
{
|
||||
private readonly IFlags _flags;
|
||||
|
||||
public FlagsmithFlagsWrapper(IFlags flags)
|
||||
{
|
||||
_flags = flags ?? throw new ArgumentNullException(nameof(flags));
|
||||
}
|
||||
|
||||
public Task<bool> IsFeatureEnabled(string featureName)
|
||||
{
|
||||
return _flags.IsFeatureEnabled(featureName);
|
||||
}
|
||||
|
||||
public Task<string?> GetFeatureValue(string featureName)
|
||||
{
|
||||
return _flags.GetFeatureValue(featureName);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
using System.Net;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using Flagsmith;
|
||||
using FluentValidation;
|
||||
using Managing.Application;
|
||||
using Managing.Application.Abstractions;
|
||||
@@ -395,6 +396,7 @@ public static class ApiBootstrap
|
||||
services.AddScoped<ISynthPredictionService, SynthPredictionService>();
|
||||
services.AddScoped<ISynthApiClient, SynthApiClient>();
|
||||
services.AddScoped<IPricesService, PricesService>();
|
||||
services.AddScoped<IFlagsmithService, FlagsmithService>();
|
||||
services.AddTransient<ICommandHandler<OpenPositionRequest, Position>, OpenPositionCommandHandler>();
|
||||
services.AddTransient<ICommandHandler<OpenSpotPositionRequest, Position>, OpenSpotPositionCommandHandler>();
|
||||
services.AddTransient<ICommandHandler<CloseBacktestFuturesPositionCommand, Position>, CloseBacktestFuturesPositionCommandHandler>();
|
||||
@@ -436,6 +438,24 @@ public static class ApiBootstrap
|
||||
sp.GetRequiredService<IOptions<InfluxDbSettings>>().Value);
|
||||
|
||||
services.Configure<KaigenSettings>(configuration.GetSection("Kaigen"));
|
||||
services.Configure<FlagsmithSettings>(configuration.GetSection("Flagsmith"));
|
||||
|
||||
// Flagsmith - Register client as Singleton
|
||||
services.AddSingleton<FlagsmithClient>(sp =>
|
||||
{
|
||||
var settings = sp.GetRequiredService<IOptions<FlagsmithSettings>>().Value;
|
||||
if (string.IsNullOrWhiteSpace(settings.ApiKey))
|
||||
{
|
||||
throw new InvalidOperationException("Flagsmith ApiKey is not configured. Please set the Flagsmith:ApiKey configuration value.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(settings.ApiUrl))
|
||||
{
|
||||
throw new InvalidOperationException("Flagsmith ApiUrl is not configured. Please set the Flagsmith:ApiUrl configuration value.");
|
||||
}
|
||||
|
||||
return new FlagsmithClient(settings.ApiKey, settings.ApiUrl);
|
||||
});
|
||||
|
||||
// Evm
|
||||
services.AddGbcFeed();
|
||||
|
||||
Reference in New Issue
Block a user