using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using C4IT.FASD.Base; namespace C4IT.F4SD { internal sealed class TicketFilterResolution { public bool Success { get; set; } public string Error { get; set; } public List RoleIds { get; set; } = new List(); } internal sealed class TicketFilterCompilationResult { public bool Success { get; set; } public string Clause { get; set; } public string Error { get; set; } } internal static class TicketFilterCompiler { internal const string ProviderName = "Matrix42"; internal const string AssignmentGroupField = "AssignmentGroup"; internal const string QueueField = "Queue"; internal const string WorkspaceField = "Workspace"; internal static async Task CompileAsync( cTicketFilterPolicy policy, Func> workspaceResolver) { var clauses = new List(); foreach (var filter in policy?.Filters ?? new List()) { if (filter == null || !filter.Enabled) continue; var providerSpecific = !string.IsNullOrWhiteSpace(filter.Provider); if (providerSpecific && !string.Equals(filter.Provider, ProviderName, StringComparison.OrdinalIgnoreCase)) return Fail($"Unsupported ticket filter provider '{filter.Provider}'."); var field = filter.Field?.Trim(); string emptyClause; string valueClause; if (!providerSpecific && string.Equals(field, AssignmentGroupField, StringComparison.OrdinalIgnoreCase)) { emptyClause = "RecipientRole IS NULL"; valueClause = BuildValueClause("RecipientRole.T(SPSSecurityClassRole).ID", "RecipientRole.T(SPSSecurityClassRole).Name", filter.Values); } else if (providerSpecific && string.Equals(field, QueueField, StringComparison.OrdinalIgnoreCase)) { emptyClause = "Queue IS NULL"; valueClause = BuildValueClause("Queue.ID", "Queue.Name", filter.Values); } else if (providerSpecific && string.Equals(field, WorkspaceField, StringComparison.OrdinalIgnoreCase)) { if (workspaceResolver == null) return Fail("The Matrix42 Workspace filter is not supported by this product version."); var resolution = await workspaceResolver(filter); if (resolution?.Success != true) return Fail(resolution?.Error ?? "The Matrix42 Workspace filter could not be resolved."); emptyClause = "RecipientRole IS NULL"; valueClause = BuildGuidClause("RecipientRole.T(SPSSecurityClassRole).ID", resolution.RoleIds); } else { return Fail($"Unsupported ticket filter field '{filter.Provider}:{field}'."); } if (filter.EmptyHandling == enumTicketFilterEmptyHandling.only) { clauses.Add($"({emptyClause})"); continue; } if (string.IsNullOrWhiteSpace(valueClause)) return Fail($"Ticket filter '{filter.Provider}:{field}' has no valid values."); var matchClause = filter.Match == enumTicketFilterMatch.exclude ? $"NOT ({valueClause})" : $"({valueClause})"; clauses.Add(filter.EmptyHandling == enumTicketFilterEmptyHandling.include ? $"(({emptyClause}) OR ({matchClause}))" : $"((NOT ({emptyClause})) AND ({matchClause}))"); } return new TicketFilterCompilationResult { Success = true, Clause = string.Join(" AND ", clauses) }; } private static string BuildValueClause(string idField, string nameField, IEnumerable values) { var items = (values ?? Enumerable.Empty()) .Where(value => value != null) .ToList(); var ids = items .Select(value => value.ID?.Trim()) .Where(value => !string.IsNullOrWhiteSpace(value)) .Distinct(StringComparer.OrdinalIgnoreCase) .Select(Quote) .ToList(); var names = items .Select(value => value.Name?.Trim()) .Where(value => !string.IsNullOrWhiteSpace(value)) .Distinct(StringComparer.OrdinalIgnoreCase) .Select(Quote) .ToList(); var clauses = new List(); if (ids.Count > 0) clauses.Add($"{idField} IN ({string.Join(", ", ids)})"); if (names.Count > 0) clauses.Add($"{nameField} IN ({string.Join(", ", names)})"); return string.Join(" OR ", clauses); } private static string BuildGuidClause(string field, IEnumerable values) { var ids = (values ?? Enumerable.Empty()) .Where(value => value != Guid.Empty) .Distinct() .Select(value => Quote(value.ToString("D"))) .ToList(); return ids.Count == 0 ? null : $"{field} IN ({string.Join(", ", ids)})"; } private static string Quote(string value) { return $"'{(value ?? string.Empty).Replace("'", "''")}'"; } private static TicketFilterCompilationResult Fail(string error) { return new TicketFilterCompilationResult { Success = false, Error = error }; } } }