diff --git a/DbFirst.API/Dashboards/SqlDashboardStorage.cs b/DbFirst.API/Dashboards/SqlDashboardStorage.cs new file mode 100644 index 0000000..fced0b2 --- /dev/null +++ b/DbFirst.API/Dashboards/SqlDashboardStorage.cs @@ -0,0 +1,106 @@ +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); + return XDocument.Parse(xml); + } + + 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(); + } +} diff --git a/DbFirst.API/Data/Dashboards/DashboardStorage.sql b/DbFirst.API/Data/Dashboards/DashboardStorage.sql new file mode 100644 index 0000000..2afd9d0 --- /dev/null +++ b/DbFirst.API/Data/Dashboards/DashboardStorage.sql @@ -0,0 +1,17 @@ +CREATE TABLE dbo.TBDD_SMF_CONFIG ( + [GUID] [bigint] IDENTITY(1,1) NOT NULL, + [ACTIVE] [bit] NOT NULL, + DashboardId NVARCHAR(128) NOT NULL, + DashboardName NVARCHAR(256) NOT NULL, + DashboardData VARBINARY(MAX) NOT NULL, + --- INSERT YOUR COLUMNS HERE --- + [ADDED_WHO] [nvarchar](50) NOT NULL, + [ADDED_WHEN] [datetime] NOT NULL, + [CHANGED_WHO] [nvarchar](50) NULL, + [CHANGED_WHEN] [datetime] NULL, + CONSTRAINT [PK_TBDD_SMF_CONFIG_DashboardStorage] PRIMARY KEY CLUSTERED +( + [GUID] ASC +)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = ON, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY] +) ON [PRIMARY] +GO diff --git a/DbFirst.API/Data/Dashboards/DefaultDashboard.xml b/DbFirst.API/Data/Dashboards/DefaultDashboard.xml index c4986d7..9c1ba8d 100644 --- a/DbFirst.API/Data/Dashboards/DefaultDashboard.xml +++ b/DbFirst.API/Data/Dashboards/DefaultDashboard.xml @@ -10,48 +10,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -91,13 +49,7 @@ - - - - - - - + \ No newline at end of file diff --git a/DbFirst.API/DbFirst.API.csproj b/DbFirst.API/DbFirst.API.csproj index 4f70300..0cd3dea 100644 --- a/DbFirst.API/DbFirst.API.csproj +++ b/DbFirst.API/DbFirst.API.csproj @@ -26,8 +26,4 @@ - - - - diff --git a/DbFirst.API/Program.cs b/DbFirst.API/Program.cs index a226710..a3405d2 100644 --- a/DbFirst.API/Program.cs +++ b/DbFirst.API/Program.cs @@ -1,4 +1,5 @@ using DbFirst.API.Middleware; +using DbFirst.API.Dashboards; using DbFirst.Application; using DbFirst.Application.Repositories; using DbFirst.Domain; @@ -10,6 +11,7 @@ using DevExpress.DashboardAspNetCore; using DevExpress.DashboardCommon; using DevExpress.DashboardWeb; using DevExpress.DataAccess.Json; +using System.Xml.Linq; var builder = WebApplication.CreateBuilder(args); @@ -108,7 +110,10 @@ builder.Services.AddScoped((IServiceProvider serviceProvi } DashboardConfigurator configurator = new DashboardConfigurator(); - configurator.SetDashboardStorage(new DashboardFileStorage(dashboardsPath)); + + var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? string.Empty; + var dashboardStorage = new SqlDashboardStorage(connectionString, "TBDD_SMF_CONFIG"); + configurator.SetDashboardStorage(dashboardStorage); DataSourceInMemoryStorage dataSourceStorage = new DataSourceInMemoryStorage(); DashboardJsonDataSource jsonDataSourceUrl = new DashboardJsonDataSource("JSON Data Source (URL)"); @@ -126,6 +131,10 @@ builder.Services.AddScoped((IServiceProvider serviceProvi dataSourceStorage.RegisterDataSource(catalogsJsonDataSource.Name, catalogsJsonDataSource.SaveToXml()); configurator.SetDataSourceStorage(dataSourceStorage); + + EnsureDashboardInStorage(dashboardStorage, "DefaultDashboard", defaultDashboardPath); + EnsureDashboardInStorage(dashboardStorage, "CatalogsGrid", catalogsGridDashboardPath); + return configurator; }); @@ -150,3 +159,15 @@ app.MapDashboardRoute("api/dashboard", "DefaultDashboard"); app.MapControllers(); app.Run(); + +static void EnsureDashboardInStorage(IEditableDashboardStorage storage, string id, string filePath) +{ + var exists = storage.GetAvailableDashboardsInfo().Any(info => string.Equals(info.ID, id, StringComparison.OrdinalIgnoreCase)); + if (exists || !File.Exists(filePath)) + { + return; + } + + var doc = XDocument.Load(filePath); + storage.AddDashboard(doc, id); +}