Files
C4IT-F4SD-M42WebApi/Shared/TicketFilterCompiler.cs
2026-07-23 12:47:28 +02:00

152 lines
6.0 KiB
C#

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<Guid> RoleIds { get; set; } = new List<Guid>();
}
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<TicketFilterCompilationResult> CompileAsync(
cTicketFilterPolicy policy,
Func<cTicketFilter, Task<TicketFilterResolution>> workspaceResolver)
{
var clauses = new List<string>();
foreach (var filter in policy?.Filters ?? new List<cTicketFilter>())
{
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<cTicketFilterValue> values)
{
var items = (values ?? Enumerable.Empty<cTicketFilterValue>())
.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<string>();
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<Guid> values)
{
var ids = (values ?? Enumerable.Empty<Guid>())
.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
};
}
}
}