Initial commit
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
using RobotNet10.CustomConfiguration.Events;
|
||||
using RobotNet10.CustomConfiguration.Models;
|
||||
|
||||
namespace RobotNet10.CustomConfiguration.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation của IConfigManager
|
||||
/// Service cho quản lý và truy vấn configuration variables
|
||||
/// </summary>
|
||||
public class ConfigManager : IConfigManager, IDisposable
|
||||
{
|
||||
private readonly IConfigService _configService;
|
||||
|
||||
// ==========================================
|
||||
// EVENTS
|
||||
// ==========================================
|
||||
|
||||
public event EventHandler<ConfigChangedEventArgs>? ConfigChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor - Subscribe vào ConfigChanged event từ IConfigService
|
||||
/// </summary>
|
||||
public ConfigManager(IConfigService configService)
|
||||
{
|
||||
_configService = configService ?? throw new ArgumentNullException(nameof(configService));
|
||||
|
||||
// Forward events from IConfigService to IConfigManager
|
||||
_configService.ConfigChanged += OnConfigServiceChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forward ConfigChanged event from IConfigService to IConfigManager subscribers
|
||||
/// </summary>
|
||||
private void OnConfigServiceChanged(object? sender, ConfigChangedEventArgs args)
|
||||
{
|
||||
ConfigChanged?.Invoke(this, args);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// CONFIG TYPE OPERATIONS
|
||||
// ==========================================
|
||||
|
||||
public async Task<ConfigFile?> GetConfigByTypeAsync(string configType)
|
||||
{
|
||||
return await _configService.GetConfigByTypeAsync(configType);
|
||||
}
|
||||
|
||||
public async Task<bool> ConfigTypeExistsAsync(string configType)
|
||||
{
|
||||
return await _configService.ConfigTypeExistsAsync(configType);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// VARIABLE EXISTENCE CHECK
|
||||
// ==========================================
|
||||
|
||||
public async Task<bool> VariableExistsAsync(string configType, string variableName)
|
||||
{
|
||||
var config = await _configService.GetConfigByTypeAsync(configType);
|
||||
if (config == null)
|
||||
return false;
|
||||
|
||||
return config.Variables.Any(v => v.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public async Task<bool> VariableExistsAsync(Guid configId, string variableName)
|
||||
{
|
||||
var config = await _configService.GetConfigByIdAsync(configId);
|
||||
if (config == null)
|
||||
return false;
|
||||
|
||||
return config.Variables.Any(v => v.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// GET VARIABLE VALUE
|
||||
// ==========================================
|
||||
|
||||
public async Task<object?> GetVariableValueAsync(string configType, string variableName)
|
||||
{
|
||||
var variable = await GetVariableAsync(configType, variableName);
|
||||
return variable?.Value;
|
||||
}
|
||||
|
||||
public async Task<object?> GetVariableValueAsync(Guid configId, string variableName)
|
||||
{
|
||||
var variable = await GetVariableAsync(configId, variableName);
|
||||
return variable?.Value;
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// GET VARIABLE(S)
|
||||
// ==========================================
|
||||
|
||||
public async Task<ConfigVariable?> GetVariableAsync(string configType, string variableName)
|
||||
{
|
||||
var config = await _configService.GetConfigByTypeAsync(configType);
|
||||
if (config == null)
|
||||
return null;
|
||||
|
||||
return config.Variables.FirstOrDefault(v => v.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public async Task<List<ConfigVariable>> GetVariablesAsync(string configType)
|
||||
{
|
||||
var config = await _configService.GetConfigByTypeAsync(configType);
|
||||
return config?.Variables ?? [];
|
||||
}
|
||||
|
||||
public async Task<ConfigVariable?> GetVariableAsync(Guid configId, string variableName)
|
||||
{
|
||||
var config = await _configService.GetConfigByIdAsync(configId);
|
||||
if (config == null)
|
||||
return null;
|
||||
|
||||
return config.Variables.FirstOrDefault(v => v.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public async Task<List<ConfigVariable>> GetVariablesAsync(Guid configId)
|
||||
{
|
||||
var config = await _configService.GetConfigByIdAsync(configId);
|
||||
return config?.Variables ?? [];
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// GET VARIABLE TYPE
|
||||
// ==========================================
|
||||
|
||||
public async Task<string?> GetVariableTypeAsync(string configType, string variableName)
|
||||
{
|
||||
var variable = await GetVariableAsync(configType, variableName);
|
||||
return variable != null ? ConvertTypeToString(variable.Type) : null;
|
||||
}
|
||||
|
||||
public async Task<string?> GetVariableTypeAsync(Guid configId, string variableName)
|
||||
{
|
||||
var variable = await GetVariableAsync(configId, variableName);
|
||||
return variable != null ? ConvertTypeToString(variable.Type) : null;
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// EVENT TRIGGERS
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Trigger ConfigChanged event
|
||||
/// Method này có thể được gọi từ bên ngoài hoặc từ các service khác khi có thay đổi
|
||||
/// </summary>
|
||||
public void OnConfigChanged(string configType, Guid configId, ConfigChangeType changeType, string? variableName = null)
|
||||
{
|
||||
var args = new ConfigChangedEventArgs
|
||||
{
|
||||
ConfigType = configType,
|
||||
ConfigId = configId,
|
||||
ChangeType = changeType,
|
||||
VariableName = variableName
|
||||
};
|
||||
|
||||
ConfigChanged?.Invoke(this, args);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// DISPOSE
|
||||
// ==========================================
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_configService.ConfigChanged -= OnConfigServiceChanged;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// HELPER METHODS
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Convert ConfigVariableType enum thành string
|
||||
/// </summary>
|
||||
private static string ConvertTypeToString(ConfigVariableType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
ConfigVariableType.String => "string",
|
||||
ConfigVariableType.Int => "int",
|
||||
ConfigVariableType.Double => "double",
|
||||
ConfigVariableType.Bool => "bool",
|
||||
ConfigVariableType.Object => "object",
|
||||
ConfigVariableType.Array => "array",
|
||||
ConfigVariableType.Enum => "enum",
|
||||
_ => throw new ArgumentException($"Unknown variable type: {type}")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
using RobotNet10.CustomConfiguration.Events;
|
||||
using RobotNet10.CustomConfiguration.Helpers;
|
||||
using RobotNet10.CustomConfiguration.Models;
|
||||
using RobotNet10.CustomConfiguration.Validators;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace RobotNet10.CustomConfiguration.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Implementation của các methods trong ConfigService
|
||||
/// </summary>
|
||||
public partial class ConfigService
|
||||
{
|
||||
public async Task<ConfigFile> CreateConfigAsync(string configType, List<ConfigVariable> variables, string? description = null)
|
||||
{
|
||||
// Validate ConfigType
|
||||
if (string.IsNullOrWhiteSpace(configType))
|
||||
{
|
||||
throw new ArgumentException("ConfigType cannot be empty", nameof(configType));
|
||||
}
|
||||
|
||||
// Check if ConfigType already exists
|
||||
var existing = await GetMetadataByTypeAsync(configType);
|
||||
if (existing != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Config with type '{configType}' already exists");
|
||||
}
|
||||
|
||||
// Create config file
|
||||
var config = new ConfigFile
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
ConfigType = configType,
|
||||
Variables = variables,
|
||||
Description = description,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow,
|
||||
FilePath = $"{ConfigPath}/{configType}{ConfigFileExtension}"
|
||||
};
|
||||
|
||||
// Validate config
|
||||
var validationResult = ConfigValidator.ValidateConfig(config);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ArgumentException($"Invalid config: {string.Join("; ", validationResult.Errors)}");
|
||||
}
|
||||
|
||||
// Save config file (includes metadata)
|
||||
await SaveConfigFileAsync(config);
|
||||
|
||||
// Trigger event
|
||||
OnConfigChanged(config, ConfigChangeType.Created);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public async Task<ConfigFile?> GetConfigByIdAsync(Guid id)
|
||||
{
|
||||
// Load all configs and find by ID
|
||||
var allMetadata = await LoadAllMetadataAsync();
|
||||
var metadata = allMetadata.FirstOrDefault(m => m.Id == id);
|
||||
if (metadata == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return await LoadConfigFileAsync(metadata.ConfigType);
|
||||
}
|
||||
|
||||
public async Task<ConfigFile?> GetConfigByTypeAsync(string configType)
|
||||
{
|
||||
return await LoadConfigFileAsync(configType);
|
||||
}
|
||||
|
||||
public async Task<List<ConfigFileMetadata>> GetAllConfigsAsync()
|
||||
{
|
||||
var metadata = await LoadAllMetadataAsync();
|
||||
return [.. metadata.OrderBy(m => m.ConfigType)];
|
||||
}
|
||||
|
||||
public async Task<List<ConfigFileMetadata>> SearchConfigsAsync(string? searchText)
|
||||
{
|
||||
var allMetadata = await GetAllConfigsAsync();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(searchText))
|
||||
{
|
||||
return allMetadata;
|
||||
}
|
||||
|
||||
var searchLower = searchText.ToLower();
|
||||
return [.. allMetadata.Where(m =>
|
||||
m.ConfigType.ToLower().Contains(searchLower) ||
|
||||
(m.Description != null && m.Description.ToLower().Contains(searchLower))
|
||||
)];
|
||||
}
|
||||
|
||||
public async Task<ConfigFile> UpdateConfigAsync(Guid id, List<ConfigVariable>? variables = null, string? description = null)
|
||||
{
|
||||
var config = await GetConfigByIdAsync(id) ?? throw new KeyNotFoundException($"Config with ID '{id}' not found");
|
||||
|
||||
// Update variables if provided
|
||||
if (variables != null)
|
||||
{
|
||||
config.Variables = variables;
|
||||
}
|
||||
|
||||
// Update description if provided
|
||||
if (description != null)
|
||||
{
|
||||
config.Description = description;
|
||||
}
|
||||
|
||||
config.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
// Validate config
|
||||
var validationResult = ConfigValidator.ValidateConfig(config);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ArgumentException($"Invalid config: {string.Join("; ", validationResult.Errors)}");
|
||||
}
|
||||
|
||||
// Save config file (includes metadata)
|
||||
await SaveConfigFileAsync(config);
|
||||
|
||||
// Trigger event
|
||||
OnConfigChanged(config, ConfigChangeType.Updated);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteConfigAsync(Guid id)
|
||||
{
|
||||
// Find config by ID
|
||||
var allMetadata = await LoadAllMetadataAsync();
|
||||
var metadata = allMetadata.FirstOrDefault(m => m.Id == id);
|
||||
if (metadata == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete config file
|
||||
// objectName should be {configType}.config so StorageManager adds .json to make {configType}.config.json
|
||||
var objectName = $"{metadata.ConfigType}.config";
|
||||
var exists = await _storageManager.ExistsAsync(ConfigPath, objectName, CancellationToken.None);
|
||||
if (exists)
|
||||
{
|
||||
await _storageManager.DeleteAsync(ConfigPath, objectName, CancellationToken.None);
|
||||
|
||||
// Trigger event
|
||||
var deletedConfig = new ConfigFile
|
||||
{
|
||||
Id = metadata.Id,
|
||||
ConfigType = metadata.ConfigType,
|
||||
CreatedAt = metadata.CreatedAt,
|
||||
UpdatedAt = metadata.UpdatedAt,
|
||||
Description = metadata.Description
|
||||
};
|
||||
OnConfigChanged(deletedConfig, ConfigChangeType.Deleted);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> ConfigTypeExistsAsync(string configType)
|
||||
{
|
||||
var metadata = await GetMetadataByTypeAsync(configType);
|
||||
return metadata != null;
|
||||
}
|
||||
|
||||
public async Task<ConfigFile> ImportConfigFromJsonAsync(Stream jsonStream, string configType)
|
||||
{
|
||||
return await ImportConfigFromJsonAsync(jsonStream, configType, null);
|
||||
}
|
||||
|
||||
public async Task<ConfigFile> ImportConfigFromJsonAsync(Stream jsonStream, string configType, string? description)
|
||||
{
|
||||
// Validate ConfigType
|
||||
if (string.IsNullOrWhiteSpace(configType))
|
||||
{
|
||||
throw new ArgumentException("ConfigType cannot be empty", nameof(configType));
|
||||
}
|
||||
|
||||
// Check if ConfigType already exists
|
||||
var existing = await GetMetadataByTypeAsync(configType);
|
||||
if (existing != null)
|
||||
{
|
||||
throw new InvalidOperationException($"Config with type '{configType}' already exists");
|
||||
}
|
||||
|
||||
// Try to parse as full config file (new format with metadata)
|
||||
// Reset stream position first
|
||||
jsonStream.Position = 0;
|
||||
string? fileDescription = null;
|
||||
List<ConfigVariable> variables;
|
||||
|
||||
try
|
||||
{
|
||||
// Try to parse as ConfigFile (new format)
|
||||
var configFile = JsonConfigParser.ParseConfigFile(jsonStream);
|
||||
// If successful, use description from file
|
||||
fileDescription = configFile.Description;
|
||||
variables = configFile.Variables;
|
||||
}
|
||||
catch (Exception ex) when (ex is JsonException or ArgumentException)
|
||||
{
|
||||
// If parsing as ConfigFile fails, try old format (array of variables)
|
||||
jsonStream.Position = 0;
|
||||
variables = JsonConfigParser.ParseVariables(jsonStream);
|
||||
}
|
||||
|
||||
// Use description from parameter if provided, otherwise use description from file
|
||||
var finalDescription = !string.IsNullOrWhiteSpace(description) ? description : fileDescription;
|
||||
|
||||
// Create config with description (from parameter or file)
|
||||
var config = await CreateConfigAsync(configType, variables, finalDescription);
|
||||
|
||||
// Note: CreateConfigAsync already triggers Created event, so no need to trigger again
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public async Task<Stream> ExportConfigToJsonAsync(Guid id)
|
||||
{
|
||||
var config = await GetConfigByIdAsync(id);
|
||||
return config == null ? throw new KeyNotFoundException($"Config with ID '{id}' not found") : await ExportConfigToJsonAsync(config);
|
||||
}
|
||||
|
||||
public Task<Stream> ExportConfigToJsonAsync(ConfigFile config)
|
||||
{
|
||||
// Export với format mới (metadata + variables)
|
||||
var json = JsonConfigParser.SerializeConfigFile(config);
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
Stream stream = new MemoryStream(bytes)
|
||||
{
|
||||
Position = 0 // Ensure stream is at the beginning
|
||||
};
|
||||
return Task.FromResult(stream);
|
||||
}
|
||||
|
||||
public async Task<ConfigFile> UpdateVariableAsync(Guid configId, string variableName, object? value)
|
||||
{
|
||||
var config = await GetConfigByIdAsync(configId) ??
|
||||
throw new KeyNotFoundException($"Config with ID '{configId}' not found");
|
||||
var variable = config.Variables.FirstOrDefault(v => v.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)) ??
|
||||
throw new KeyNotFoundException($"Variable '{variableName}' not found in config");
|
||||
variable.Value = value;
|
||||
config.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
// Validate
|
||||
var validationResult = ConfigValidator.ValidateConfig(config);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ArgumentException($"Invalid config: {string.Join("; ", validationResult.Errors)}");
|
||||
}
|
||||
|
||||
// Save config file (includes metadata)
|
||||
await SaveConfigFileAsync(config);
|
||||
|
||||
// Trigger event
|
||||
OnConfigChanged(config, ConfigChangeType.VariableUpdated, variableName);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public async Task<ConfigFile> AddVariableAsync(Guid configId, ConfigVariable variable)
|
||||
{
|
||||
var config = await GetConfigByIdAsync(configId) ?? throw new KeyNotFoundException($"Config with ID '{configId}' not found");
|
||||
|
||||
// Check if variable name already exists
|
||||
if (config.Variables.Any(v => v.Name.Equals(variable.Name, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
throw new InvalidOperationException($"Variable '{variable.Name}' already exists in config");
|
||||
}
|
||||
|
||||
config.Variables.Add(variable);
|
||||
config.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
// Validate
|
||||
var validationResult = ConfigValidator.ValidateConfig(config);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ArgumentException($"Invalid config: {string.Join("; ", validationResult.Errors)}");
|
||||
}
|
||||
|
||||
// Save config file (includes metadata)
|
||||
await SaveConfigFileAsync(config);
|
||||
|
||||
// Trigger event
|
||||
OnConfigChanged(config, ConfigChangeType.VariableAdded, variable.Name);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
public async Task<ConfigFile> RemoveVariableAsync(Guid configId, string variableName)
|
||||
{
|
||||
var config = await GetConfigByIdAsync(configId) ??
|
||||
throw new KeyNotFoundException($"Config with ID '{configId}' not found");
|
||||
var variable = config.Variables.FirstOrDefault(v => v.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)) ??
|
||||
throw new KeyNotFoundException($"Variable '{variableName}' not found in config");
|
||||
config.Variables.Remove(variable);
|
||||
config.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
// Validate
|
||||
var validationResult = ConfigValidator.ValidateConfig(config);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ArgumentException($"Invalid config: {string.Join("; ", validationResult.Errors)}");
|
||||
}
|
||||
|
||||
// Save config file (includes metadata)
|
||||
await SaveConfigFileAsync(config);
|
||||
|
||||
// Trigger event
|
||||
OnConfigChanged(config, ConfigChangeType.VariableRemoved, variableName);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// PRIVATE HELPER METHODS
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Load config file từ StorageManager (format mới với metadata)
|
||||
/// </summary>
|
||||
private async Task<ConfigFile?> LoadConfigFileAsync(string configType)
|
||||
{
|
||||
try
|
||||
{
|
||||
// objectName should be {configType}.config so StorageManager finds {configType}.config.json
|
||||
var objectName = $"{configType}.config";
|
||||
var exists = await _storageManager.ExistsAsync(ConfigPath, objectName, CancellationToken.None);
|
||||
if (!exists)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get file stream from StorageManager (works with both local and remote storage)
|
||||
using var stream = await _storageManager.GetFileAsync(ConfigPath, objectName, CancellationToken.None);
|
||||
|
||||
// Parse config file directly from stream (format mới với metadata)
|
||||
return JsonConfigParser.ParseConfigFile(stream);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// Config file corrupted/invalid format - treat as not found
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save config file vào StorageManager (format mới với metadata)
|
||||
/// </summary>
|
||||
private async Task SaveConfigFileAsync(ConfigFile config)
|
||||
{
|
||||
// Serialize với format mới (metadata + variables)
|
||||
var json = JsonConfigParser.SerializeConfigFile(config);
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
|
||||
// objectName should be {configType}.config so StorageManager adds .json to make {configType}.config.json
|
||||
var objectName = $"{config.ConfigType}.config";
|
||||
|
||||
using var stream = new MemoryStream(bytes);
|
||||
await _storageManager.UploadAsync(
|
||||
ConfigPath,
|
||||
objectName,
|
||||
stream,
|
||||
stream.Length,
|
||||
"application/json",
|
||||
CancellationToken.None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using RobotNet10.CustomConfiguration.Helpers;
|
||||
using RobotNet10.CustomConfiguration.Models;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace RobotNet10.CustomConfiguration.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Helper methods cho quản lý metadata (load từ config files)
|
||||
/// </summary>
|
||||
public partial class ConfigService
|
||||
{
|
||||
/// <summary>
|
||||
/// Load tất cả config files và trả về metadata
|
||||
/// </summary>
|
||||
private async Task<List<ConfigFileMetadata>> LoadAllMetadataAsync()
|
||||
{
|
||||
var metadataList = new List<ConfigFileMetadata>();
|
||||
|
||||
try
|
||||
{
|
||||
// List all files in configs directory
|
||||
var files = await _storageManager.ListAsync(ConfigPath, recursive: false, CancellationToken.None);
|
||||
|
||||
// Filter only .config.json files
|
||||
var configFiles = files.Where(f => f.EndsWith(ConfigFileExtension, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
foreach (var fileName in configFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Extract configType from filename: {configType}.config.json
|
||||
var configType = fileName.Replace(ConfigFileExtension, "", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// Load config file to get metadata
|
||||
var config = await LoadConfigFileAsync(configType);
|
||||
if (config != null)
|
||||
{
|
||||
metadataList.Add(new ConfigFileMetadata
|
||||
{
|
||||
Id = config.Id,
|
||||
ConfigType = config.ConfigType,
|
||||
CreatedAt = config.CreatedAt,
|
||||
UpdatedAt = config.UpdatedAt,
|
||||
Description = config.Description
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// Skip corrupted config files
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
|
||||
{
|
||||
// Storage unavailable - return what we have so far
|
||||
return metadataList;
|
||||
}
|
||||
|
||||
return metadataList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get metadata by ConfigType (load từ config file)
|
||||
/// </summary>
|
||||
private async Task<ConfigFileMetadata?> GetMetadataByTypeAsync(string configType)
|
||||
{
|
||||
var config = await LoadConfigFileAsync(configType);
|
||||
if (config == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ConfigFileMetadata
|
||||
{
|
||||
Id = config.Id,
|
||||
ConfigType = config.ConfigType,
|
||||
CreatedAt = config.CreatedAt,
|
||||
UpdatedAt = config.UpdatedAt,
|
||||
Description = config.Description
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using RobotNet10.CustomConfiguration.Events;
|
||||
using RobotNet10.CustomConfiguration.Models;
|
||||
using RobotNet10.StorageManager;
|
||||
|
||||
namespace RobotNet10.CustomConfiguration.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service implementation cho quản lý configuration files
|
||||
/// Sử dụng StorageManager để lưu trữ file JSON
|
||||
/// </summary>
|
||||
public partial class ConfigService : IConfigService, IDisposable
|
||||
{
|
||||
private readonly StorageManager.StorageManager _storageManager;
|
||||
private const string ConfigPath = "configs"; // Path trong StorageManager
|
||||
private const string ConfigFileExtension = ".config.json"; // Extension cho config files
|
||||
private const string StorageConfigsKey = "StorageConfigs"; // Named options key cho IOptionsMonitor
|
||||
|
||||
// ==========================================
|
||||
// EVENTS
|
||||
// ==========================================
|
||||
|
||||
public event EventHandler<ConfigChangedEventArgs>? ConfigChanged;
|
||||
|
||||
public ConfigService(IOptionsMonitor<StorageConfig> optionsSnapshot)
|
||||
{
|
||||
var config = optionsSnapshot.Get(StorageConfigsKey);
|
||||
|
||||
ArgumentNullException.ThrowIfNull(config);
|
||||
|
||||
_storageManager = new StorageManager.StorageManager(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger ConfigChanged event
|
||||
/// </summary>
|
||||
protected virtual void OnConfigChanged(ConfigFile config, ConfigChangeType changeType, string? variableName = null)
|
||||
{
|
||||
var args = new ConfigChangedEventArgs
|
||||
{
|
||||
ConfigType = config.ConfigType,
|
||||
ConfigId = config.Id,
|
||||
ChangeType = changeType,
|
||||
VariableName = variableName
|
||||
};
|
||||
|
||||
ConfigChanged?.Invoke(this, args);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_storageManager?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
using RobotNet10.CustomConfiguration.Events;
|
||||
using RobotNet10.CustomConfiguration.Models;
|
||||
|
||||
namespace RobotNet10.CustomConfiguration.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service interface cho quản lý và truy vấn configuration variables
|
||||
/// </summary>
|
||||
public interface IConfigManager
|
||||
{
|
||||
// ==========================================
|
||||
// EVENTS
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Event được trigger khi có thay đổi trong config (tạo mới, cập nhật, xóa, hoặc thay đổi variables)
|
||||
/// </summary>
|
||||
event EventHandler<ConfigChangedEventArgs>? ConfigChanged;
|
||||
|
||||
// ==========================================
|
||||
// CONFIG TYPE OPERATIONS
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Lấy config theo ConfigType (đầy đủ thông tin bao gồm variables)
|
||||
/// </summary>
|
||||
Task<ConfigFile?> GetConfigByTypeAsync(string configType);
|
||||
|
||||
/// <summary>
|
||||
/// Kiểm tra ConfigType có tồn tại không
|
||||
/// </summary>
|
||||
Task<bool> ConfigTypeExistsAsync(string configType);
|
||||
|
||||
// ==========================================
|
||||
// VARIABLE EXISTENCE CHECK
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Kiểm tra variable có tồn tại không (theo ConfigType)
|
||||
/// </summary>
|
||||
Task<bool> VariableExistsAsync(string configType, string variableName);
|
||||
|
||||
/// <summary>
|
||||
/// Kiểm tra variable có tồn tại không (theo ConfigId)
|
||||
/// </summary>
|
||||
Task<bool> VariableExistsAsync(Guid configId, string variableName);
|
||||
|
||||
// ==========================================
|
||||
// GET VARIABLE VALUE
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Lấy giá trị (Value) của variable (theo ConfigType)
|
||||
/// </summary>
|
||||
Task<object?> GetVariableValueAsync(string configType, string variableName);
|
||||
|
||||
/// <summary>
|
||||
/// Lấy giá trị (Value) của variable (theo ConfigId)
|
||||
/// </summary>
|
||||
Task<object?> GetVariableValueAsync(Guid configId, string variableName);
|
||||
|
||||
// ==========================================
|
||||
// GET VARIABLE(S)
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Lấy một variable cụ thể (theo ConfigType)
|
||||
/// </summary>
|
||||
Task<ConfigVariable?> GetVariableAsync(string configType, string variableName);
|
||||
|
||||
/// <summary>
|
||||
/// Lấy tất cả variables của một ConfigType
|
||||
/// </summary>
|
||||
Task<List<ConfigVariable>> GetVariablesAsync(string configType);
|
||||
|
||||
/// <summary>
|
||||
/// Lấy một variable cụ thể (theo ConfigId)
|
||||
/// </summary>
|
||||
Task<ConfigVariable?> GetVariableAsync(Guid configId, string variableName);
|
||||
|
||||
/// <summary>
|
||||
/// Lấy tất cả variables của một ConfigId
|
||||
/// </summary>
|
||||
Task<List<ConfigVariable>> GetVariablesAsync(Guid configId);
|
||||
|
||||
// ==========================================
|
||||
// GET VARIABLE TYPE
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Lấy kiểu dữ liệu của variable dưới dạng string (theo ConfigType)
|
||||
/// Trả về: "string", "int", "double", "bool", "object", "array", "enum"
|
||||
/// </summary>
|
||||
Task<string?> GetVariableTypeAsync(string configType, string variableName);
|
||||
|
||||
/// <summary>
|
||||
/// Lấy kiểu dữ liệu của variable dưới dạng string (theo ConfigId)
|
||||
/// Trả về: "string", "int", "double", "bool", "object", "array", "enum"
|
||||
/// </summary>
|
||||
Task<string?> GetVariableTypeAsync(Guid configId, string variableName);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using RobotNet10.CustomConfiguration.Events;
|
||||
using RobotNet10.CustomConfiguration.Models;
|
||||
|
||||
namespace RobotNet10.CustomConfiguration.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service interface cho quản lý configuration files
|
||||
/// </summary>
|
||||
public interface IConfigService
|
||||
{
|
||||
// ==========================================
|
||||
// EVENTS
|
||||
// ==========================================
|
||||
|
||||
/// <summary>
|
||||
/// Event được trigger khi có thay đổi trong config (tạo mới, cập nhật, xóa, hoặc thay đổi variables)
|
||||
/// </summary>
|
||||
event EventHandler<ConfigChangedEventArgs>? ConfigChanged;
|
||||
|
||||
// ==========================================
|
||||
// CONFIG FILE MANAGEMENT
|
||||
// ==========================================
|
||||
|
||||
Task<ConfigFile> CreateConfigAsync(string configType, List<ConfigVariable> variables, string? description = null);
|
||||
Task<ConfigFile?> GetConfigByIdAsync(Guid id);
|
||||
Task<ConfigFile?> GetConfigByTypeAsync(string configType);
|
||||
Task<List<ConfigFileMetadata>> GetAllConfigsAsync();
|
||||
Task<List<ConfigFileMetadata>> SearchConfigsAsync(string? searchText);
|
||||
Task<ConfigFile> UpdateConfigAsync(Guid id, List<ConfigVariable>? variables = null, string? description = null);
|
||||
Task<bool> DeleteConfigAsync(Guid id);
|
||||
Task<bool> ConfigTypeExistsAsync(string configType);
|
||||
|
||||
// ==========================================
|
||||
// IMPORT/EXPORT
|
||||
// ==========================================
|
||||
|
||||
Task<ConfigFile> ImportConfigFromJsonAsync(Stream jsonStream, string configType);
|
||||
Task<ConfigFile> ImportConfigFromJsonAsync(Stream jsonStream, string configType, string? description);
|
||||
Task<Stream> ExportConfigToJsonAsync(Guid id);
|
||||
Task<Stream> ExportConfigToJsonAsync(ConfigFile config);
|
||||
|
||||
// ==========================================
|
||||
// VARIABLE MANAGEMENT
|
||||
// ==========================================
|
||||
|
||||
Task<ConfigFile> UpdateVariableAsync(Guid configId, string variableName, object? value);
|
||||
Task<ConfigFile> AddVariableAsync(Guid configId, ConfigVariable variable);
|
||||
Task<ConfigFile> RemoveVariableAsync(Guid configId, string variableName);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user