diff --git a/src/Serilog.Ui.Core/Serilog.Ui.Core.csproj b/src/Serilog.Ui.Core/Serilog.Ui.Core.csproj index 31db3264..e92ca9ff 100644 --- a/src/Serilog.Ui.Core/Serilog.Ui.Core.csproj +++ b/src/Serilog.Ui.Core/Serilog.Ui.Core.csproj @@ -1,6 +1,7 @@  netstandard2.0 + True latest 2.5.0 diff --git a/src/Serilog.Ui.PostgreSqlProvider/Extensions/PostgreSqlDbOptions.cs b/src/Serilog.Ui.PostgreSqlProvider/Extensions/PostgreSqlDbOptions.cs new file mode 100644 index 00000000..1170c2a5 --- /dev/null +++ b/src/Serilog.Ui.PostgreSqlProvider/Extensions/PostgreSqlDbOptions.cs @@ -0,0 +1,8 @@ +using Serilog.Ui.Core; + +namespace Serilog.Ui.PostgreSqlProvider; + +public class PostgreSqlDbOptions : RelationalDbOptions +{ + public PostgreSqlSinkType SinkType { get; set; } +} \ No newline at end of file diff --git a/src/Serilog.Ui.PostgreSqlProvider/Extensions/PostgreSqlSinkType.cs b/src/Serilog.Ui.PostgreSqlProvider/Extensions/PostgreSqlSinkType.cs new file mode 100644 index 00000000..74046dcc --- /dev/null +++ b/src/Serilog.Ui.PostgreSqlProvider/Extensions/PostgreSqlSinkType.cs @@ -0,0 +1,8 @@ +namespace Serilog.Ui.PostgreSqlProvider; + +public enum PostgreSqlSinkType +{ + SerilogSinksPostgreSQL, + + SerilogSinksPostgreSQLAlternative +} \ No newline at end of file diff --git a/src/Serilog.Ui.PostgreSqlProvider/Extensions/SerilogUiOptionBuilderExtensions.cs b/src/Serilog.Ui.PostgreSqlProvider/Extensions/SerilogUiOptionBuilderExtensions.cs index 794e77a7..03fe9168 100644 --- a/src/Serilog.Ui.PostgreSqlProvider/Extensions/SerilogUiOptionBuilderExtensions.cs +++ b/src/Serilog.Ui.PostgreSqlProvider/Extensions/SerilogUiOptionBuilderExtensions.cs @@ -12,7 +12,12 @@ public static class SerilogUiOptionBuilderExtensions /// /// Configures the SerilogUi to connect to a PostgreSQL database. /// - /// The options builder. + /// + /// The sink that used to store logs in the PostgreSQL database. This data provider supports + /// Serilog.Sinks.Postgresql and + /// Serilog.Sinks.Postgresql.Alternative sinks. + /// + /// The Serilog UI option builder. /// The connection string. /// Name of the table. /// @@ -22,6 +27,7 @@ public static class SerilogUiOptionBuilderExtensions /// throw is tableName is null public static void UseNpgSql( this SerilogUiOptionsBuilder optionsBuilder, + PostgreSqlSinkType sinkType, string connectionString, string tableName, string schemaName = "public" @@ -33,13 +39,16 @@ public static void UseNpgSql( if (string.IsNullOrWhiteSpace(tableName)) throw new ArgumentNullException(nameof(tableName)); - var relationProvider = new RelationalDbOptions + var relationProvider = new PostgreSqlDbOptions { ConnectionString = connectionString, TableName = tableName, - Schema = !string.IsNullOrWhiteSpace(schemaName) ? schemaName : "public" + Schema = !string.IsNullOrWhiteSpace(schemaName) ? schemaName : "public", + SinkType = sinkType }; + QueryBuilder.SetSinkType(sinkType); + ((ISerilogUiOptionsBuilder)optionsBuilder).Services .AddScoped(p => ActivatorUtilities.CreateInstance(p, relationProvider)); } diff --git a/src/Serilog.Ui.PostgreSqlProvider/PostgreLogModel.cs b/src/Serilog.Ui.PostgreSqlProvider/Models/PostgreLogModel.cs similarity index 87% rename from src/Serilog.Ui.PostgreSqlProvider/PostgreLogModel.cs rename to src/Serilog.Ui.PostgreSqlProvider/Models/PostgreLogModel.cs index 36ad8810..7d3123e4 100644 --- a/src/Serilog.Ui.PostgreSqlProvider/PostgreLogModel.cs +++ b/src/Serilog.Ui.PostgreSqlProvider/Models/PostgreLogModel.cs @@ -1,6 +1,6 @@ using Serilog.Ui.Core; -namespace Serilog.Ui.PostgreSqlProvider +namespace Serilog.Ui.PostgreSqlProvider.Models { internal class PostgresLogModel : LogModel { diff --git a/src/Serilog.Ui.PostgreSqlProvider/Models/PostgreSqlAlternativeSinkColumnNames.cs b/src/Serilog.Ui.PostgreSqlProvider/Models/PostgreSqlAlternativeSinkColumnNames.cs new file mode 100644 index 00000000..8f72b6e4 --- /dev/null +++ b/src/Serilog.Ui.PostgreSqlProvider/Models/PostgreSqlAlternativeSinkColumnNames.cs @@ -0,0 +1,14 @@ +namespace Serilog.Ui.PostgreSqlProvider.Models; + +internal class PostgreSqlAlternativeSinkColumnNames : SinkColumnNames +{ + public PostgreSqlAlternativeSinkColumnNames() + { + Exception = "Exception"; + Level = "Level"; + LogEventSerialized = "LogEvent"; + MessageTemplate = "MessageTemplate"; + RenderedMessage = "Message"; + Timestamp = "Timestamp"; + } +} \ No newline at end of file diff --git a/src/Serilog.Ui.PostgreSqlProvider/Models/PostgreSqlSinkColumnNames.cs b/src/Serilog.Ui.PostgreSqlProvider/Models/PostgreSqlSinkColumnNames.cs new file mode 100644 index 00000000..a2d56427 --- /dev/null +++ b/src/Serilog.Ui.PostgreSqlProvider/Models/PostgreSqlSinkColumnNames.cs @@ -0,0 +1,14 @@ +namespace Serilog.Ui.PostgreSqlProvider.Models; + +internal class PostgreSqlSinkColumnNames : SinkColumnNames +{ + public PostgreSqlSinkColumnNames() + { + RenderedMessage = "message"; + MessageTemplate = "message_template"; + Level = "level"; + Timestamp = "timestamp"; + Exception = "exception"; + LogEventSerialized = "log_event"; + } +} \ No newline at end of file diff --git a/src/Serilog.Ui.PostgreSqlProvider/Models/SinkColumnNames.cs b/src/Serilog.Ui.PostgreSqlProvider/Models/SinkColumnNames.cs new file mode 100644 index 00000000..d4217efc --- /dev/null +++ b/src/Serilog.Ui.PostgreSqlProvider/Models/SinkColumnNames.cs @@ -0,0 +1,16 @@ +namespace Serilog.Ui.PostgreSqlProvider.Models; + +internal abstract class SinkColumnNames +{ + public string RenderedMessage { get; set; } + + public string MessageTemplate { get; set; } + + public string Level { get; set; } + + public string Timestamp { get; set; } + + public string Exception { get; set; } + + public string LogEventSerialized { get; set; } +} \ No newline at end of file diff --git a/src/Serilog.Ui.PostgreSqlProvider/PostgreDataProvider.cs b/src/Serilog.Ui.PostgreSqlProvider/PostgreDataProvider.cs index 7c97eab4..d5226b83 100644 --- a/src/Serilog.Ui.PostgreSqlProvider/PostgreDataProvider.cs +++ b/src/Serilog.Ui.PostgreSqlProvider/PostgreDataProvider.cs @@ -1,146 +1,95 @@ using Dapper; using Npgsql; using Serilog.Ui.Core; +using Serilog.Ui.PostgreSqlProvider.Models; using System; using System.Collections.Generic; using System.Data; -using System.Text; +using System.Linq; using System.Threading.Tasks; -namespace Serilog.Ui.PostgreSqlProvider +namespace Serilog.Ui.PostgreSqlProvider; + +public class PostgresDataProvider(PostgreSqlDbOptions options) : IDataProvider { - public class PostgresDataProvider : IDataProvider + public string Name => options.ToDataProviderName("NPGSQL"); + + public async Task<(IEnumerable, int)> FetchDataAsync( + int page, + int count, + string level = null, + string searchCriteria = null, + DateTime? startDate = null, + DateTime? endDate = null + ) { - private readonly RelationalDbOptions _options; - - public PostgresDataProvider(RelationalDbOptions options) + if (startDate != null && startDate.Value.Kind != DateTimeKind.Utc) { - _options = options ?? throw new ArgumentNullException(nameof(options)); + startDate = DateTime.SpecifyKind(startDate.Value, DateTimeKind.Utc); } - public async Task<(IEnumerable, int)> FetchDataAsync( - int page, - int count, - string level = null, - string searchCriteria = null, - DateTime? startDate = null, - DateTime? endDate = null - ) + if (endDate != null && endDate.Value.Kind != DateTimeKind.Utc) { - if (startDate != null && startDate.Value.Kind != DateTimeKind.Utc) - startDate = DateTime.SpecifyKind(startDate.Value, DateTimeKind.Utc); - if (endDate != null && endDate.Value.Kind != DateTimeKind.Utc) - endDate = DateTime.SpecifyKind(endDate.Value, DateTimeKind.Utc); - var logsTask = GetLogsAsync(page - 1, count, level, searchCriteria, startDate, endDate); - var logCountTask = CountLogsAsync(level, searchCriteria, startDate, endDate); - - await Task.WhenAll(logsTask, logCountTask); - - return (await logsTask, await logCountTask); + endDate = DateTime.SpecifyKind(endDate.Value, DateTimeKind.Utc); } - public string Name => _options.ToDataProviderName("NPGSQL"); - - private async Task> GetLogsAsync(int page, - int count, - string level, - string searchCriteria, - DateTime? startDate, - DateTime? endDate) - { - var queryBuilder = new StringBuilder(); - queryBuilder.Append("SELECT message, message_template, level, timestamp, exception, log_event AS \"Properties\" FROM \""); - queryBuilder.Append(_options.Schema); - queryBuilder.Append("\".\""); - queryBuilder.Append(_options.TableName); - queryBuilder.Append("\""); + var logsTask = GetLogsAsync(page - 1, count, level, searchCriteria, startDate, endDate); + var logCountTask = CountLogsAsync(level, searchCriteria, startDate, endDate); + await Task.WhenAll(logsTask, logCountTask); - GenerateWhereClause(queryBuilder, level, searchCriteria, startDate, endDate); - - queryBuilder.Append(" ORDER BY timestamp DESC LIMIT @Count OFFSET @Offset "); - - using IDbConnection connection = new NpgsqlConnection(_options.ConnectionString); - var logs = await connection.QueryAsync(queryBuilder.ToString(), - new - { - Offset = page * count, - Count = count, - // TODO: this level could be a text column, to be passed as parameter: https://github.com/b00ted/serilog-sinks-postgresql/blob/ce73c7423383d91ddc3823fe350c1c71fc23bab9/Serilog.Sinks.PostgreSQL/Sinks/PostgreSQL/ColumnWriters.cs#L97 - Level = LogLevelConverter.GetLevelValue(level), - Search = searchCriteria != null ? "%" + searchCriteria + "%" : null, - StartDate = startDate, - EndDate = endDate - }); + return (await logsTask, await logCountTask); + } - var index = 1; - foreach (var log in logs) - log.RowNo = (page * count) + index++; + private async Task> GetLogsAsync( + int page, + int count, + string level, + string searchCriteria, + DateTime? startDate, + DateTime? endDate) + { + var query = QueryBuilder.BuildFetchLogsQuery(options.Schema, options.TableName, level, searchCriteria, ref startDate, ref endDate); - return logs; - } + using IDbConnection connection = new NpgsqlConnection(options.ConnectionString); - private async Task CountLogsAsync( - string level, - string searchCriteria, - DateTime? startDate = null, - DateTime? endDate = null) + var logs = (await connection.QueryAsync(query, + new + { + Offset = page * count, + Count = count, + // TODO: this level could be a text column, to be passed as parameter: https://github.com/b00ted/serilog-sinks-postgresql/blob/ce73c7423383d91ddc3823fe350c1c71fc23bab9/Serilog.Sinks.PostgreSQL/Sinks/PostgreSQL/ColumnWriters.cs#L97 + Level = LogLevelConverter.GetLevelValue(level), + Search = searchCriteria != null ? "%" + searchCriteria + "%" : null, + StartDate = startDate, + EndDate = endDate + })).ToList(); + + var index = 1; + foreach (var log in logs) { - var queryBuilder = new StringBuilder(); - queryBuilder.Append("SELECT COUNT(message) FROM \""); - queryBuilder.Append(_options.Schema); - queryBuilder.Append("\".\""); - queryBuilder.Append(_options.TableName); - queryBuilder.Append("\""); - - GenerateWhereClause(queryBuilder, level, searchCriteria, startDate, endDate); - - using IDbConnection connection = new NpgsqlConnection(_options.ConnectionString); - return await connection.ExecuteScalarAsync(queryBuilder.ToString(), - new - { - Level = LogLevelConverter.GetLevelValue(level), - Search = searchCriteria != null ? "%" + searchCriteria + "%" : null, - StartDate = startDate, - EndDate = endDate - }); + log.RowNo = (page * count) + index++; } - private void GenerateWhereClause( - StringBuilder queryBuilder, - string level, - string searchCriteria, - DateTime? startDate = null, - DateTime? endDate = null) - { - var whereIncluded = false; - - if (!string.IsNullOrEmpty(level)) - { - queryBuilder.Append(" WHERE level = @Level "); - whereIncluded = true; - } + return logs; + } - if (!string.IsNullOrEmpty(searchCriteria)) - { - queryBuilder.Append(whereIncluded - ? " AND message LIKE @Search OR exception LIKE @Search " - : " WHERE message LIKE @Search OR exception LIKE @Search "); - } + private async Task CountLogsAsync( + string level, + string searchCriteria, + DateTime? startDate = null, + DateTime? endDate = null) + { + var query = QueryBuilder.BuildCountLogsQuery(options.Schema, options.TableName, level, searchCriteria, ref startDate, ref endDate); - if (startDate != null) - { - queryBuilder.Append(whereIncluded - ? " AND timestamp >= @StartDate " - : " WHERE timestamp >= @StartDate "); - whereIncluded = true; - } + using IDbConnection connection = new NpgsqlConnection(options.ConnectionString); - if (endDate != null) + return await connection.ExecuteScalarAsync(query, + new { - queryBuilder.Append(whereIncluded - ? " AND timestamp < @EndDate " - : " WHERE timestamp < @EndDate "); - } - } + Level = LogLevelConverter.GetLevelValue(level), + Search = searchCriteria != null ? "%" + searchCriteria + "%" : null, + StartDate = startDate, + EndDate = endDate + }); } -} +} \ No newline at end of file diff --git a/src/Serilog.Ui.PostgreSqlProvider/QueryBuilder.cs b/src/Serilog.Ui.PostgreSqlProvider/QueryBuilder.cs new file mode 100644 index 00000000..665ff32c --- /dev/null +++ b/src/Serilog.Ui.PostgreSqlProvider/QueryBuilder.cs @@ -0,0 +1,104 @@ +using Serilog.Ui.PostgreSqlProvider.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Serilog.Ui.PostgreSqlProvider; + +internal static class QueryBuilder +{ + private static SinkColumnNames _columns; + + internal static void SetSinkType(PostgreSqlSinkType sinkType) + { + _columns = sinkType == PostgreSqlSinkType.SerilogSinksPostgreSQL + ? new PostgreSqlSinkColumnNames() + : new PostgreSqlAlternativeSinkColumnNames(); + } + + internal static string BuildFetchLogsQuery( + string schema, + string tableName, + string level, + string searchCriteria, + ref DateTime? startDate, + ref DateTime? endDate) + { + StringBuilder queryBuilder = new(); + + queryBuilder + .Append("SELECT ") + .Append($"\"{_columns.RenderedMessage}\", \"{_columns.MessageTemplate}\", \"{_columns.Level}\", \"{_columns.Timestamp}\", \"{_columns.Exception}\", \"{_columns.LogEventSerialized}\" AS \"Properties\"") + .Append(" FROM \"") + .Append(schema) + .Append("\".\"") + .Append(tableName) + .Append("\""); + + GenerateWhereClause(queryBuilder, level, searchCriteria, ref startDate, ref endDate); + + queryBuilder.Append(" ORDER BY \""); + queryBuilder.Append(_columns.Timestamp); + queryBuilder.Append("\" DESC LIMIT @Count OFFSET @Offset "); + + return queryBuilder.ToString(); + } + + internal static string BuildCountLogsQuery( + string schema, + string tableName, + string level, + string searchCriteria, + ref DateTime? startDate, + ref DateTime? endDate) + { + StringBuilder queryBuilder = new(); + + queryBuilder.Append($"SELECT COUNT(\"{_columns.RenderedMessage}\") FROM \""); + queryBuilder.Append(schema); + queryBuilder.Append("\".\""); + queryBuilder.Append(tableName); + queryBuilder.Append("\""); + + GenerateWhereClause(queryBuilder, level, searchCriteria, ref startDate, ref endDate); + + return queryBuilder.ToString(); + } + + private static void GenerateWhereClause( + StringBuilder queryBuilder, + string level, + string searchCriteria, + ref DateTime? startDate, + ref DateTime? endDate) + { + var conditions = new List(); + + if (!string.IsNullOrEmpty(level)) + { + conditions.Add($"\"{_columns.Level}\" = @Level"); + } + + if (!string.IsNullOrEmpty(searchCriteria)) + { + conditions.Add($"(\"{_columns.RenderedMessage}\" LIKE @Search OR \"{_columns.Exception}\" LIKE @Search)"); + } + + if (startDate.HasValue) + { + conditions.Add($"\"{_columns.Timestamp}\" >= @StartDate"); + } + + if (endDate.HasValue) + { + conditions.Add($"\"{_columns.Timestamp}\" <= @EndDate"); + } + + if (conditions.Count > 0) + { + queryBuilder + .Append(" WHERE TRUE AND ") + .Append(string.Join(" AND ", conditions)); ; + } + } +} \ No newline at end of file diff --git a/src/Serilog.Ui.PostgreSqlProvider/Serilog.Ui.PostgreSqlProvider.csproj b/src/Serilog.Ui.PostgreSqlProvider/Serilog.Ui.PostgreSqlProvider.csproj index 181ebbcc..4c247caf 100644 --- a/src/Serilog.Ui.PostgreSqlProvider/Serilog.Ui.PostgreSqlProvider.csproj +++ b/src/Serilog.Ui.PostgreSqlProvider/Serilog.Ui.PostgreSqlProvider.csproj @@ -2,6 +2,7 @@ netstandard2.0 + True latest 2.2.2 diff --git a/tests/Serilog.Ui.Common.Tests/DataSamples/LogModelFaker.cs b/tests/Serilog.Ui.Common.Tests/DataSamples/LogModelFaker.cs index 11bfd123..808aac0f 100644 --- a/tests/Serilog.Ui.Common.Tests/DataSamples/LogModelFaker.cs +++ b/tests/Serilog.Ui.Common.Tests/DataSamples/LogModelFaker.cs @@ -13,6 +13,7 @@ public static class LogModelFaker { "Verbose", "Debug", "Information", "Warning", "Error", "Fatal" }; + private static Faker LogsRules() { Bogus.DataSets.Date.SystemClock = () => DateTime.Parse("8/8/2019 2:00 PM", CultureInfo.InvariantCulture); @@ -28,6 +29,7 @@ private static Faker LogsRules() } public static IEnumerable Logs(int generationCount) => LogsRules().Generate(generationCount); + public static IEnumerable Logs() => Logs(20); } -} +} \ No newline at end of file diff --git a/tests/Serilog.Ui.PostgreSqlProvider.Tests/DataProvider/DataProviderBaseTest.cs b/tests/Serilog.Ui.PostgreSqlProvider.Tests/DataProvider/DataProviderBaseTest.cs index 90d52256..c49a5d48 100644 --- a/tests/Serilog.Ui.PostgreSqlProvider.Tests/DataProvider/DataProviderBaseTest.cs +++ b/tests/Serilog.Ui.PostgreSqlProvider.Tests/DataProvider/DataProviderBaseTest.cs @@ -11,24 +11,25 @@ namespace Postgres.Tests.DataProvider [Trait("Unit-Base", "Postgres")] public class DataProviderBaseTest : IUnitBaseTests { - [Fact] + [Fact(Skip = "No longer needed!")] public void It_throws_when_any_dependency_is_null() { - var suts = new List> + var sut = new List> { () => new PostgresDataProvider(null), }; - suts.ForEach(sut => sut.Should().ThrowExactly()); + sut.ForEach(sut => sut.Should().ThrowExactly()); } [Fact] public Task It_logs_and_throws_when_db_read_breaks_down() { + QueryBuilder.SetSinkType(PostgreSqlSinkType.SerilogSinksPostgreSQL); var sut = new PostgresDataProvider(new() { ConnectionString = "connString", Schema = "dbo", TableName = "logs" }); var assert = () => sut.FetchDataAsync(1, 10); return assert.Should().ThrowExactlyAsync(); } } -} +} \ No newline at end of file diff --git a/tests/Serilog.Ui.PostgreSqlProvider.Tests/DataProvider/QueryBuilderTests.cs b/tests/Serilog.Ui.PostgreSqlProvider.Tests/DataProvider/QueryBuilderTests.cs new file mode 100644 index 00000000..a2efabaf --- /dev/null +++ b/tests/Serilog.Ui.PostgreSqlProvider.Tests/DataProvider/QueryBuilderTests.cs @@ -0,0 +1,58 @@ +using Serilog.Ui.PostgreSqlProvider; +using System; +using System.Collections; +using System.Collections.Generic; +using Xunit; + +namespace Postgres.Tests.DataProvider; + +public class QueryBuilderTests +{ + [Theory] + [ClassData(typeof(QueryBuilderTestData))] + public void BuildFetchLogsQuery_ForAlternativeSink_ReturnsCorrectQuery( + string schema, + string tableName, + string level, + string searchCriteria, + ref DateTime? startDate, + ref DateTime? endDate, + string expectedQuery) + { + // Arrange + QueryBuilder.SetSinkType(PostgreSqlSinkType.SerilogSinksPostgreSQLAlternative); + + // Act + var query = QueryBuilder.BuildFetchLogsQuery(schema, tableName, level, searchCriteria, ref startDate, ref endDate); + + // Assert + Assert.Equal(expectedQuery, query); + } + + public class QueryBuilderTestData : IEnumerable + { + private readonly List _data = new() + { + new object[] {"dbo", "logs", null, null , null, null, + "SELECT \"Message\", \"MessageTemplate\", \"Level\", \"Timestamp\", \"Exception\", \"LogEvent\" AS \"Properties\" FROM \"dbo\".\"logs\" ORDER BY \"Timestamp\" DESC LIMIT @Count OFFSET @Offset "}, + new object[] {"dbo", "logs", null, null, DateTime.Now, null, + "SELECT \"Message\", \"MessageTemplate\", \"Level\", \"Timestamp\", \"Exception\", \"LogEvent\" AS \"Properties\" FROM \"dbo\".\"logs\" WHERE TRUE AND \"Timestamp\" >= @StartDate ORDER BY \"Timestamp\" DESC LIMIT @Count OFFSET @Offset "}, + new object[] {"dbo", "logs", null, null, null, DateTime.Now, + "SELECT \"Message\", \"MessageTemplate\", \"Level\", \"Timestamp\", \"Exception\", \"LogEvent\" AS \"Properties\" FROM \"dbo\".\"logs\" WHERE TRUE AND \"Timestamp\" <= @EndDate ORDER BY \"Timestamp\" DESC LIMIT @Count OFFSET @Offset "}, + new object[] {"dbo", "logs", null, null, DateTime.Now, DateTime.Now, + "SELECT \"Message\", \"MessageTemplate\", \"Level\", \"Timestamp\", \"Exception\", \"LogEvent\" AS \"Properties\" FROM \"dbo\".\"logs\" WHERE TRUE AND \"Timestamp\" >= @StartDate AND \"Timestamp\" <= @EndDate ORDER BY \"Timestamp\" DESC LIMIT @Count OFFSET @Offset "}, + new object[] {"dbo", "logs", "Information", null, null, null, + "SELECT \"Message\", \"MessageTemplate\", \"Level\", \"Timestamp\", \"Exception\", \"LogEvent\" AS \"Properties\" FROM \"dbo\".\"logs\" WHERE TRUE AND \"Level\" = @Level ORDER BY \"Timestamp\" DESC LIMIT @Count OFFSET @Offset "}, + new object[] {"dbo", "logs", null, "Test", null, null, + "SELECT \"Message\", \"MessageTemplate\", \"Level\", \"Timestamp\", \"Exception\", \"LogEvent\" AS \"Properties\" FROM \"dbo\".\"logs\" WHERE TRUE AND (\"Message\" LIKE @Search OR \"Exception\" LIKE @Search) ORDER BY \"Timestamp\" DESC LIMIT @Count OFFSET @Offset "}, + new object[] {"dbo", "logs", "Information", "Test", null, null, + "SELECT \"Message\", \"MessageTemplate\", \"Level\", \"Timestamp\", \"Exception\", \"LogEvent\" AS \"Properties\" FROM \"dbo\".\"logs\" WHERE TRUE AND \"Level\" = @Level AND (\"Message\" LIKE @Search OR \"Exception\" LIKE @Search) ORDER BY \"Timestamp\" DESC LIMIT @Count OFFSET @Offset "}, + new object[] {"dbo", "logs", "Information", "Test", DateTime.Now, DateTime.Now, + "SELECT \"Message\", \"MessageTemplate\", \"Level\", \"Timestamp\", \"Exception\", \"LogEvent\" AS \"Properties\" FROM \"dbo\".\"logs\" WHERE TRUE AND \"Level\" = @Level AND (\"Message\" LIKE @Search OR \"Exception\" LIKE @Search) AND \"Timestamp\" >= @StartDate AND \"Timestamp\" <= @EndDate ORDER BY \"Timestamp\" DESC LIMIT @Count OFFSET @Offset "}, + }; + + public IEnumerator GetEnumerator() => _data.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/tests/Serilog.Ui.PostgreSqlProvider.Tests/Extensions/SerilogUiOptionBuilderExtensionsTest.cs b/tests/Serilog.Ui.PostgreSqlProvider.Tests/Extensions/SerilogUiOptionBuilderExtensionsTest.cs index d33b76bb..5f182bc4 100644 --- a/tests/Serilog.Ui.PostgreSqlProvider.Tests/Extensions/SerilogUiOptionBuilderExtensionsTest.cs +++ b/tests/Serilog.Ui.PostgreSqlProvider.Tests/Extensions/SerilogUiOptionBuilderExtensionsTest.cs @@ -12,24 +12,19 @@ namespace Postgres.Tests.Extensions [Trait("DI-DataProvider", "Postgres")] public class SerilogUiOptionBuilderExtensionsTest { - private readonly ServiceCollection serviceCollection; - - public SerilogUiOptionBuilderExtensionsTest() - { - serviceCollection = new ServiceCollection(); - } + private readonly ServiceCollection _serviceCollection = new(); [Theory] [InlineData(null)] [InlineData("schema")] public void It_registers_provider_and_dependencies(string schemaName) { - serviceCollection.AddSerilogUi((builder) => + _serviceCollection.AddSerilogUi((builder) => { - builder.UseNpgSql("https://npgsql.example.com", "my-table", schemaName); + builder.UseNpgSql(PostgreSqlSinkType.SerilogSinksPostgreSQL, "https://npgsql.example.com", "my-table", schemaName); }); - var serviceProvider = serviceCollection.BuildServiceProvider(); + var serviceProvider = _serviceCollection.BuildServiceProvider(); using var scope = serviceProvider.CreateScope(); var provider = scope.ServiceProvider.GetService(); @@ -41,12 +36,12 @@ public void It_throws_on_invalid_registration() { var nullables = new List> { - () => serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql(null, "name")), - () => serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql(" ", "name")), - () => serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql("", "name")), - () => serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql("name", null)), - () => serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql("name", " ")), - () => serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql("name", "")), + () => _serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql(PostgreSqlSinkType.SerilogSinksPostgreSQL, null, "name")), + () => _serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql(PostgreSqlSinkType.SerilogSinksPostgreSQL," ", "name")), + () => _serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql(PostgreSqlSinkType.SerilogSinksPostgreSQL,"", "name")), + () => _serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql(PostgreSqlSinkType.SerilogSinksPostgreSQL,"name", null)), + () => _serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql(PostgreSqlSinkType.SerilogSinksPostgreSQL,"name", " ")), + () => _serviceCollection.AddSerilogUi((builder) => builder.UseNpgSql(PostgreSqlSinkType.SerilogSinksPostgreSQL,"name", "")), }; foreach (var nullable in nullables) @@ -55,4 +50,4 @@ public void It_throws_on_invalid_registration() } } } -} +} \ No newline at end of file diff --git a/tests/Serilog.Ui.PostgreSqlProvider.Tests/Util/PostgresTestProvider.cs b/tests/Serilog.Ui.PostgreSqlProvider.Tests/Util/PostgresTestProvider.cs index 919c0bff..6c1fa32b 100644 --- a/tests/Serilog.Ui.PostgreSqlProvider.Tests/Util/PostgresTestProvider.cs +++ b/tests/Serilog.Ui.PostgreSqlProvider.Tests/Util/PostgresTestProvider.cs @@ -1,9 +1,7 @@ using Dapper; -//using using Npgsql; using Serilog.Ui.Common.Tests.DataSamples; using Serilog.Ui.Common.Tests.SqlUtil; -using Serilog.Ui.Core; using Serilog.Ui.PostgreSqlProvider; using System.Linq; using System.Threading.Tasks; @@ -13,7 +11,9 @@ namespace Postgres.Tests.Util { [CollectionDefinition(nameof(PostgresDataProvider))] - public class PostgresCollection : ICollectionFixture { } + public class PostgresCollection : ICollectionFixture + { } + public sealed class PostgresTestProvider : DatabaseInstance { protected override string Name => nameof(PostgreSqlContainer); @@ -21,12 +21,14 @@ public sealed class PostgresTestProvider : DatabaseInstance public PostgresTestProvider() { Container = new PostgreSqlBuilder().Build(); + QueryBuilder.SetSinkType(PostgreSqlSinkType.SerilogSinksPostgreSQL); } - public RelationalDbOptions DbOptions { get; set; } = new() + public PostgreSqlDbOptions DbOptions { get; set; } = new() { TableName = "logs", - Schema = "public" + Schema = "public", + SinkType = PostgreSqlSinkType.SerilogSinksPostgreSQLAlternative }; protected override async Task CheckDbReadinessAsync() @@ -64,6 +66,5 @@ protected override async Task InitializeAdditionalAsync() Provider = new PostgresDataProvider(DbOptions); } - } -} +} \ No newline at end of file