Add flagsmith
This commit is contained in:
@@ -651,7 +651,6 @@ public class BacktestController : BaseController
|
|||||||
{
|
{
|
||||||
var user = await GetUser();
|
var user = await GetUser();
|
||||||
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(request.UniversalConfig.ScenarioName) && request.UniversalConfig.Scenario == null)
|
if (string.IsNullOrEmpty(request.UniversalConfig.ScenarioName) && request.UniversalConfig.Scenario == null)
|
||||||
{
|
{
|
||||||
return BadRequest("Either scenario name or scenario object is required in universal configuration");
|
return BadRequest("Either scenario name or scenario object is required in universal configuration");
|
||||||
|
|||||||
@@ -31,6 +31,10 @@
|
|||||||
"DebitEndpoint": "/api/credits/debit",
|
"DebitEndpoint": "/api/credits/debit",
|
||||||
"RefundEndpoint": "/api/credits/refund"
|
"RefundEndpoint": "/api/credits/refund"
|
||||||
},
|
},
|
||||||
|
"Flagsmith": {
|
||||||
|
"ApiKey": "ser.ShJJJMtWYS9fwuzd83ejwR",
|
||||||
|
"ApiUrl": "https://flag.kaigen.ai/api/v1/"
|
||||||
|
},
|
||||||
"N8n": {
|
"N8n": {
|
||||||
"WebhookUrl": "https://n8n.kai.managing.live/webhook/fa9308b6-983b-42ec-b085-71599d655951",
|
"WebhookUrl": "https://n8n.kai.managing.live/webhook/fa9308b6-983b-42ec-b085-71599d655951",
|
||||||
"IndicatorRequestWebhookUrl": "https://n8n.kai.managing.live/webhook/3aa07b66-1e64-46a7-8618-af300914cb11",
|
"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>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentValidation" Version="11.9.1"/>
|
<PackageReference Include="FluentValidation" Version="11.9.1"/>
|
||||||
|
<PackageReference Include="Flagsmith" Version="5.0.0"/>
|
||||||
<PackageReference Include="GeneticSharp" Version="3.1.4"/>
|
<PackageReference Include="GeneticSharp" Version="3.1.4"/>
|
||||||
<PackageReference Include="MediatR" Version="12.2.0"/>
|
<PackageReference Include="MediatR" Version="12.2.0"/>
|
||||||
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="11.1.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 System.Reflection;
|
||||||
using Discord.Commands;
|
using Discord.Commands;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
|
using Flagsmith;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Managing.Application;
|
using Managing.Application;
|
||||||
using Managing.Application.Abstractions;
|
using Managing.Application.Abstractions;
|
||||||
@@ -395,6 +396,7 @@ public static class ApiBootstrap
|
|||||||
services.AddScoped<ISynthPredictionService, SynthPredictionService>();
|
services.AddScoped<ISynthPredictionService, SynthPredictionService>();
|
||||||
services.AddScoped<ISynthApiClient, SynthApiClient>();
|
services.AddScoped<ISynthApiClient, SynthApiClient>();
|
||||||
services.AddScoped<IPricesService, PricesService>();
|
services.AddScoped<IPricesService, PricesService>();
|
||||||
|
services.AddScoped<IFlagsmithService, FlagsmithService>();
|
||||||
services.AddTransient<ICommandHandler<OpenPositionRequest, Position>, OpenPositionCommandHandler>();
|
services.AddTransient<ICommandHandler<OpenPositionRequest, Position>, OpenPositionCommandHandler>();
|
||||||
services.AddTransient<ICommandHandler<OpenSpotPositionRequest, Position>, OpenSpotPositionCommandHandler>();
|
services.AddTransient<ICommandHandler<OpenSpotPositionRequest, Position>, OpenSpotPositionCommandHandler>();
|
||||||
services.AddTransient<ICommandHandler<CloseBacktestFuturesPositionCommand, Position>, CloseBacktestFuturesPositionCommandHandler>();
|
services.AddTransient<ICommandHandler<CloseBacktestFuturesPositionCommand, Position>, CloseBacktestFuturesPositionCommandHandler>();
|
||||||
@@ -436,6 +438,24 @@ public static class ApiBootstrap
|
|||||||
sp.GetRequiredService<IOptions<InfluxDbSettings>>().Value);
|
sp.GetRequiredService<IOptions<InfluxDbSettings>>().Value);
|
||||||
|
|
||||||
services.Configure<KaigenSettings>(configuration.GetSection("Kaigen"));
|
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
|
// Evm
|
||||||
services.AddGbcFeed();
|
services.AddGbcFeed();
|
||||||
|
|||||||
Reference in New Issue
Block a user