< Summary

Information
Class: Elsa.Diagnostics.StructuredLogs.Persistence.Relational.Services.RelationalStructuredLogSqlBuilder
Assembly: Elsa.Diagnostics.StructuredLogs.Persistence.Relational
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Diagnostics.StructuredLogs.Persistence.Relational/Services/RelationalStructuredLogSqlBuilder.cs
Line coverage
100%
Covered lines: 83
Uncovered lines: 0
Coverable lines: 83
Total lines: 136
Line coverage: 100%
Branch coverage
100%
Covered branches: 20
Total branches: 20
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
BuildInsert()100%11100%
BuildQuery(...)100%22100%
BuildListSources()100%11100%
BuildDeleteOlderThan(...)100%11100%
BuildDeleteRowsBeyondMax(...)100%11100%
BuildFilterPredicates(...)100%1414100%
AddStringPredicate(...)100%22100%
AddPredicate(...)100%22100%
.cctor()100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Diagnostics.StructuredLogs.Persistence.Relational/Services/RelationalStructuredLogSqlBuilder.cs

#LineLine coverage
 1using Elsa.Diagnostics.StructuredLogs.Models;
 2using Elsa.Diagnostics.StructuredLogs.Persistence.Relational.Contracts;
 3
 4namespace Elsa.Diagnostics.StructuredLogs.Persistence.Relational.Services;
 5
 126public class RelationalStructuredLogSqlBuilder(IRelationalStructuredLogDialect dialect)
 7{
 128    private readonly string _table = dialect.QuoteIdentifier("StructuredLogEvents");
 9
 10    public string BuildInsert()
 11    {
 112        var columns = Columns;
 113        var columnList = string.Join(", ", columns.Select(dialect.QuoteIdentifier));
 2114        var parameterList = string.Join(", ", columns.Select(x => $"{dialect.ParameterPrefix}{x}"));
 115        return $"INSERT INTO {_table} ({columnList}) VALUES ({parameterList})";
 16    }
 17
 18    public QueryDefinition BuildQuery(StructuredLogFilter filter)
 19    {
 720        var parameters = new Dictionary<string, object?>();
 721        var predicates = BuildFilterPredicates(filter, parameters);
 722        var where = predicates.Count == 0 ? "" : $" WHERE {string.Join(" AND ", predicates)}";
 723        var limit = Math.Clamp(filter.Take ?? 100, 0, 1000);
 724        var sql = $"SELECT {string.Join(", ", Columns.Select(dialect.QuoteIdentifier))} FROM {_table}{where} ORDER BY {d
 725        sql = dialect.ApplyLimit(sql, limit);
 726        return new(sql, parameters);
 27    }
 28
 29    public string BuildListSources()
 30    {
 131        var sourceId = dialect.QuoteIdentifier("SourceId");
 132        var receivedAt = dialect.QuoteIdentifier("ReceivedAt");
 133        return $"SELECT {sourceId}, MAX({receivedAt}) AS {dialect.QuoteIdentifier("LastSeen")} FROM {_table} GROUP BY {s
 34    }
 35
 36    public QueryDefinition BuildDeleteOlderThan(string cutoff)
 37    {
 138        return new($"DELETE FROM {_table} WHERE {dialect.QuoteIdentifier("ReceivedAt")} < {dialect.ParameterPrefix}Cutof
 39    }
 40
 41    public QueryDefinition BuildDeleteRowsBeyondMax(int maxRows)
 42    {
 143        var id = dialect.QuoteIdentifier("Id");
 144        var receivedAt = dialect.QuoteIdentifier("ReceivedAt");
 145        var sequence = dialect.QuoteIdentifier("Sequence");
 146        var selectSql = $"SELECT {id} FROM {_table} ORDER BY {receivedAt} DESC, {sequence} DESC, {id} DESC";
 147        var sql = $"DELETE FROM {_table} WHERE {id} IN ({dialect.ApplyOffset(selectSql, maxRows)})";
 148        return new(sql, new Dictionary<string, object?>());
 49    }
 50
 51    private List<string> BuildFilterPredicates(StructuredLogFilter filter, IDictionary<string, object?> parameters)
 52    {
 753        var predicates = new List<string>();
 54
 755        if (filter.MinimumLevel is { } minimumLevel)
 156            AddPredicate(predicates, parameters, "Level", ">=", (int)minimumLevel);
 57
 758        if (filter.Levels is { Count: > 0 })
 59        {
 160            var names = filter.Levels.Select((level, index) =>
 161            {
 262                var name = $"Level{index}";
 263                parameters[name] = (int)level;
 264                return $"{dialect.ParameterPrefix}{name}";
 165            });
 166            predicates.Add($"{dialect.QuoteIdentifier("Level")} IN ({string.Join(", ", names)})");
 67        }
 68
 769        if (!string.IsNullOrWhiteSpace(filter.CategoryPrefix))
 170            AddPredicate(predicates, parameters, "Category", "LIKE", $"{filter.CategoryPrefix}%");
 71
 772        if (!string.IsNullOrWhiteSpace(filter.Text))
 73        {
 274            parameters["Text"] = $"%{filter.Text}%";
 275            var textParameter = $"{dialect.ParameterPrefix}Text";
 276            predicates.Add($"({dialect.QuoteIdentifier("Message")} LIKE {textParameter} OR {dialect.QuoteIdentifier("Mes
 77        }
 78
 779        AddStringPredicate(predicates, parameters, "TenantId", filter.TenantId);
 780        AddStringPredicate(predicates, parameters, "WorkflowDefinitionId", filter.WorkflowDefinitionId);
 781        AddStringPredicate(predicates, parameters, "WorkflowInstanceId", filter.WorkflowInstanceId);
 782        AddStringPredicate(predicates, parameters, "TraceId", filter.TraceId);
 783        AddStringPredicate(predicates, parameters, "SpanId", filter.SpanId);
 784        AddStringPredicate(predicates, parameters, "CorrelationId", filter.CorrelationId);
 785        AddStringPredicate(predicates, parameters, "SourceId", filter.SourceId);
 86
 787        if (filter.From is { } from)
 188            AddPredicate(predicates, parameters, "Timestamp", ">=", RelationalStructuredLogMapper.FormatTimestamp(from),
 89
 790        if (filter.To is { } to)
 191            AddPredicate(predicates, parameters, "Timestamp", "<=", RelationalStructuredLogMapper.FormatTimestamp(to), "
 92
 793        return predicates;
 94    }
 95
 96    private void AddStringPredicate(ICollection<string> predicates, IDictionary<string, object?> parameters, string colu
 97    {
 4998        if (string.IsNullOrWhiteSpace(value))
 4099            return;
 100
 9101        AddPredicate(predicates, parameters, column, "=", value);
 9102    }
 103
 104    private void AddPredicate(ICollection<string> predicates, IDictionary<string, object?> parameters, string column, st
 105    {
 13106        var name = parameterName ?? column;
 13107        predicates.Add($"{dialect.QuoteIdentifier(column)} {op} {dialect.ParameterPrefix}{name}");
 13108        parameters[name] = value;
 13109    }
 110
 1111    private static readonly string[] Columns =
 1112    [
 1113        "Id",
 1114        "Sequence",
 1115        "Timestamp",
 1116        "ReceivedAt",
 1117        "Level",
 1118        "Category",
 1119        "EventId",
 1120        "EventName",
 1121        "Message",
 1122        "MessageTemplate",
 1123        "ExceptionJson",
 1124        "ScopesJson",
 1125        "PropertiesJson",
 1126        "TraceId",
 1127        "SpanId",
 1128        "CorrelationId",
 1129        "TenantId",
 1130        "WorkflowDefinitionId",
 1131        "WorkflowInstanceId",
 1132        "SourceId"
 1133    ];
 134}
 135
 136public record QueryDefinition(string Sql, IReadOnlyDictionary<string, object?> Parameters);