using System.Data; using System.Text; using System.Xml.Linq; using DevExpress.DashboardWeb; using Microsoft.Data.SqlClient; namespace DbFirst.API.Dashboards; public sealed class SqlDashboardStorage : IEditableDashboardStorage { private readonly string _connectionString; private readonly string _tableName; private readonly Func? _userProvider; public SqlDashboardStorage(string connectionString, string tableName = "TBDD_SMF_CONFIG", Func? userProvider = null) { _connectionString = connectionString; _tableName = tableName; _userProvider = userProvider; } public IEnumerable GetAvailableDashboardsInfo() { var dashboards = new List(); using var connection = new SqlConnection(_connectionString); using var command = new SqlCommand($"SELECT DashboardId, DashboardName FROM dbo.[{_tableName}] WHERE ACTIVE = 1 ORDER BY DashboardName", connection); connection.Open(); using var reader = command.ExecuteReader(); while (reader.Read()) { var id = reader.GetString(0); var name = reader.GetString(1); dashboards.Add(new DashboardInfo { ID = id, Name = name }); } return dashboards; } public XDocument LoadDashboard(string dashboardId) { using var connection = new SqlConnection(_connectionString); using var command = new SqlCommand($"SELECT DashboardData FROM dbo.[{_tableName}] WHERE DashboardId = @Id AND ACTIVE = 1", connection); command.Parameters.Add(new SqlParameter("@Id", SqlDbType.NVarChar, 128) { Value = dashboardId }); connection.Open(); var data = command.ExecuteScalar() as byte[]; if (data == null) { throw new ArgumentException($"Dashboard '{dashboardId}' not found."); } var xml = Encoding.UTF8.GetString(data); var doc = XDocument.Parse(xml); NormalizeCatalogDateDimensions(doc); return doc; } private static void NormalizeCatalogDateDimensions(XDocument doc) { var dateMembers = new HashSet(StringComparer.OrdinalIgnoreCase) { "AddedWhen", "ChangedWhen" }; foreach (var dimension in doc.Descendants("Dimension")) { var member = dimension.Attribute("DataMember")?.Value; if (member == null || !dateMembers.Contains(member)) { continue; } var interval = dimension.Attribute("DateTimeGroupInterval")?.Value; if (string.IsNullOrWhiteSpace(interval) || string.Equals(interval, "Year", StringComparison.OrdinalIgnoreCase)) { dimension.SetAttributeValue("DateTimeGroupInterval", "DayMonthYear"); } } } public string AddDashboard(XDocument dashboard, string dashboardName) { var id = string.IsNullOrWhiteSpace(dashboardName) ? Guid.NewGuid().ToString("N") : dashboardName; var payload = Encoding.UTF8.GetBytes(dashboard.ToString(SaveOptions.DisableFormatting)); var userName = _userProvider?.Invoke(); using var connection = new SqlConnection(_connectionString); using var command = new SqlCommand($"INSERT INTO dbo.[{_tableName}] (ACTIVE, DashboardId, DashboardName, DashboardData, ADDED_WHO, ADDED_WHEN) VALUES (1, @Id, @Name, @Data, COALESCE(@User, SUSER_SNAME()), SYSUTCDATETIME())", connection); command.Parameters.Add(new SqlParameter("@Id", SqlDbType.NVarChar, 128) { Value = id }); command.Parameters.Add(new SqlParameter("@Name", SqlDbType.NVarChar, 256) { Value = string.IsNullOrWhiteSpace(dashboardName) ? id : dashboardName }); command.Parameters.Add(new SqlParameter("@Data", SqlDbType.VarBinary, -1) { Value = payload }); command.Parameters.Add(new SqlParameter("@User", SqlDbType.NVarChar, 50) { Value = (object?)userName ?? DBNull.Value }); connection.Open(); command.ExecuteNonQuery(); return id; } public void SaveDashboard(string dashboardId, XDocument dashboard) { var payload = Encoding.UTF8.GetBytes(dashboard.ToString(SaveOptions.DisableFormatting)); var userName = _userProvider?.Invoke(); using var connection = new SqlConnection(_connectionString); using var command = new SqlCommand($"UPDATE dbo.[{_tableName}] SET DashboardData = @Data, CHANGED_WHO = COALESCE(@User, SUSER_SNAME()), CHANGED_WHEN = SYSUTCDATETIME() WHERE DashboardId = @Id", connection); command.Parameters.Add(new SqlParameter("@Id", SqlDbType.NVarChar, 128) { Value = dashboardId }); command.Parameters.Add(new SqlParameter("@Data", SqlDbType.VarBinary, -1) { Value = payload }); command.Parameters.Add(new SqlParameter("@User", SqlDbType.NVarChar, 50) { Value = (object?)userName ?? DBNull.Value }); connection.Open(); var rows = command.ExecuteNonQuery(); if (rows == 0) { throw new ArgumentException($"Dashboard '{dashboardId}' not found."); } } public void DeleteDashboard(string dashboardId) { using var connection = new SqlConnection(_connectionString); using var command = new SqlCommand($"DELETE FROM dbo.[{_tableName}] WHERE DashboardId = @Id", connection); command.Parameters.Add(new SqlParameter("@Id", SqlDbType.NVarChar, 128) { Value = dashboardId }); connection.Open(); command.ExecuteNonQuery(); } }