Add provider-aware activity filter API
This commit is contained in:
151
Shared/ActivityFilterCompiler.cs
Normal file
151
Shared/ActivityFilterCompiler.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using C4IT.FASD.Base;
|
||||
|
||||
namespace C4IT.F4SD
|
||||
{
|
||||
internal sealed class ActivityFilterResolution
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Error { get; set; }
|
||||
public List<Guid> RoleIds { get; set; } = new List<Guid>();
|
||||
}
|
||||
|
||||
internal sealed class ActivityFilterCompilationResult
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Clause { get; set; }
|
||||
public string Error { get; set; }
|
||||
}
|
||||
|
||||
internal static class ActivityFilterCompiler
|
||||
{
|
||||
internal const string ProviderName = "Matrix42";
|
||||
internal const string AssignmentGroupField = "AssignmentGroup";
|
||||
internal const string QueueField = "Queue";
|
||||
internal const string WorkspaceField = "Workspace";
|
||||
|
||||
internal static async Task<ActivityFilterCompilationResult> CompileAsync(
|
||||
cActivityFilterPolicy policy,
|
||||
Func<cActivityFilter, Task<ActivityFilterResolution>> workspaceResolver)
|
||||
{
|
||||
var clauses = new List<string>();
|
||||
foreach (var filter in policy?.Filters ?? new List<cActivityFilter>())
|
||||
{
|
||||
if (filter == null || !filter.Enabled)
|
||||
continue;
|
||||
|
||||
var providerSpecific = !string.IsNullOrWhiteSpace(filter.Provider);
|
||||
if (providerSpecific && !string.Equals(filter.Provider, ProviderName, StringComparison.OrdinalIgnoreCase))
|
||||
return Fail($"Unsupported activity 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 activity filter field '{filter.Provider}:{field}'.");
|
||||
}
|
||||
|
||||
if (filter.EmptyHandling == enumActivityFilterEmptyHandling.only)
|
||||
{
|
||||
clauses.Add($"({emptyClause})");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(valueClause))
|
||||
return Fail($"Activity filter '{filter.Provider}:{field}' has no valid values.");
|
||||
|
||||
var matchClause = filter.Match == enumActivityFilterMatch.exclude
|
||||
? $"NOT ({valueClause})"
|
||||
: $"({valueClause})";
|
||||
|
||||
clauses.Add(filter.EmptyHandling == enumActivityFilterEmptyHandling.include
|
||||
? $"(({emptyClause}) OR ({matchClause}))"
|
||||
: $"((NOT ({emptyClause})) AND ({matchClause}))");
|
||||
}
|
||||
|
||||
return new ActivityFilterCompilationResult
|
||||
{
|
||||
Success = true,
|
||||
Clause = string.Join(" AND ", clauses)
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildValueClause(string idField, string nameField, IEnumerable<cActivityFilterValue> values)
|
||||
{
|
||||
var items = (values ?? Enumerable.Empty<cActivityFilterValue>())
|
||||
.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 ActivityFilterCompilationResult Fail(string error)
|
||||
{
|
||||
return new ActivityFilterCompilationResult
|
||||
{
|
||||
Success = false,
|
||||
Error = error
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user