Rename WebApi activity filters to ticket filters
This commit is contained in:
179
F4SDM42WebApi/TicketFilterService.cs
Normal file
179
F4SDM42WebApi/TicketFilterService.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.Logging;
|
||||
|
||||
using update4u.SPS.DataLayer;
|
||||
|
||||
using static C4IT.Logging.cLogManager;
|
||||
|
||||
namespace C4IT.F4SD
|
||||
{
|
||||
public partial class F4SDHelperService
|
||||
{
|
||||
private sealed class WorkspaceRoleCacheEntry
|
||||
{
|
||||
public DateTime ExpiresAtUtc { get; set; }
|
||||
public List<Guid> RoleIds { get; set; }
|
||||
}
|
||||
|
||||
private static readonly object workspaceRoleCacheLock = new object();
|
||||
private static readonly Dictionary<string, WorkspaceRoleCacheEntry> workspaceRoleCache =
|
||||
new Dictionary<string, WorkspaceRoleCacheEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
internal async Task<string> ApplyTicketFiltersAsync(string baseFilter, cTicketFilterPolicy policy)
|
||||
{
|
||||
var result = await TicketFilterCompiler.CompileAsync(policy, ResolveWorkspaceRolesAsync);
|
||||
if (!result.Success)
|
||||
{
|
||||
LogEntry($"Ticket filter rejected (fail closed): {result.Error}", LogLevels.Error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return string.IsNullOrWhiteSpace(result.Clause)
|
||||
? baseFilter
|
||||
: $"({baseFilter}) AND ({result.Clause})";
|
||||
}
|
||||
|
||||
internal cTicketFilterCapabilities GetTicketFilterCapabilities()
|
||||
{
|
||||
var capabilities = new cTicketFilterCapabilities
|
||||
{
|
||||
Provider = TicketFilterCompiler.ProviderName,
|
||||
CommonFields = new List<string> { TicketFilterCompiler.AssignmentGroupField },
|
||||
ProviderFields = new List<string> { TicketFilterCompiler.QueueField }
|
||||
};
|
||||
|
||||
if (SPSDataEngineSchemaReader.ClassGetIDFromName("ESMWorkspaceClassBase") != Guid.Empty)
|
||||
capabilities.ProviderFields.Add(TicketFilterCompiler.WorkspaceField);
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
private async Task<TicketFilterResolution> ResolveWorkspaceRolesAsync(cTicketFilter filter)
|
||||
{
|
||||
await Task.Delay(0);
|
||||
var workspaceClassId = SPSDataEngineSchemaReader.ClassGetIDFromName("ESMWorkspaceClassBase");
|
||||
if (workspaceClassId == Guid.Empty)
|
||||
return WorkspaceFailure("ESMWorkspaceClassBase is not available.");
|
||||
|
||||
var roleIds = new HashSet<Guid>();
|
||||
foreach (var value in filter?.Values ?? new List<cTicketFilterValue>())
|
||||
{
|
||||
var key = !string.IsNullOrWhiteSpace(value?.ID)
|
||||
? "id:" + value.ID.Trim()
|
||||
: "name:" + value?.Name?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(key) || key.EndsWith(":", StringComparison.Ordinal))
|
||||
return WorkspaceFailure("A Workspace filter value has neither ID nor Name.");
|
||||
|
||||
if (TryGetCachedWorkspaceRoles(key, out var cached))
|
||||
{
|
||||
roleIds.UnionWith(cached);
|
||||
continue;
|
||||
}
|
||||
|
||||
DataTable table;
|
||||
try
|
||||
{
|
||||
table = LoadWorkspaceRoles(
|
||||
workspaceClassId,
|
||||
!string.IsNullOrWhiteSpace(value.ID)
|
||||
? $"ID = '{Escape(value.ID.Trim())}'"
|
||||
: $"Title = '{Escape(value.Name.Trim())}'");
|
||||
|
||||
// IDs are stable and therefore preferred. The name is kept as a
|
||||
// portable fallback for configurations imported into another ESM.
|
||||
if ((table?.Rows == null || table.Rows.Count == 0)
|
||||
&& !string.IsNullOrWhiteSpace(value.ID)
|
||||
&& !string.IsNullOrWhiteSpace(value.Name))
|
||||
{
|
||||
table = LoadWorkspaceRoles(
|
||||
workspaceClassId,
|
||||
$"Title = '{Escape(value.Name.Trim())}'");
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
LogException(exception);
|
||||
return WorkspaceFailure($"Workspace relation ESMWorkspaceClassBase.Roles could not be loaded for '{key}'.");
|
||||
}
|
||||
|
||||
if (table?.Rows == null || table.Rows.Count == 0)
|
||||
return WorkspaceFailure($"Workspace '{key}' was not found or has no resolvable Roles relation.");
|
||||
|
||||
var workspaceIds = table.Rows.Cast<DataRow>()
|
||||
.Select(row => getGuidFromObject(row["WorkspaceID"]))
|
||||
.Where(id => id != Guid.Empty)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
if (workspaceIds.Count != 1)
|
||||
return WorkspaceFailure($"Workspace '{key}' is not unique.");
|
||||
|
||||
var resolved = table.Rows.Cast<DataRow>()
|
||||
.Select(row => getGuidFromObject(row["RoleID"]))
|
||||
.Where(id => id != Guid.Empty)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
if (resolved.Count == 0)
|
||||
return WorkspaceFailure($"Workspace '{key}' contains no roles.");
|
||||
|
||||
SetCachedWorkspaceRoles(key, resolved);
|
||||
roleIds.UnionWith(resolved);
|
||||
}
|
||||
|
||||
return new TicketFilterResolution
|
||||
{
|
||||
Success = true,
|
||||
RoleIds = roleIds.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
private static DataTable LoadWorkspaceRoles(Guid workspaceClassId, string where)
|
||||
{
|
||||
return FragmentRequestBase.SimpleLoad(
|
||||
workspaceClassId,
|
||||
"ID as WorkspaceID, Title as WorkspaceTitle, Roles.T(SPSSecurityClassRole).ID as RoleID",
|
||||
where);
|
||||
}
|
||||
|
||||
private static bool TryGetCachedWorkspaceRoles(string key, out List<Guid> roleIds)
|
||||
{
|
||||
lock (workspaceRoleCacheLock)
|
||||
{
|
||||
if (workspaceRoleCache.TryGetValue(key, out var entry) && entry.ExpiresAtUtc > DateTime.UtcNow)
|
||||
{
|
||||
roleIds = new List<Guid>(entry.RoleIds);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
roleIds = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void SetCachedWorkspaceRoles(string key, List<Guid> roleIds)
|
||||
{
|
||||
lock (workspaceRoleCacheLock)
|
||||
{
|
||||
workspaceRoleCache[key] = new WorkspaceRoleCacheEntry
|
||||
{
|
||||
ExpiresAtUtc = DateTime.UtcNow.AddMinutes(5),
|
||||
RoleIds = new List<Guid>(roleIds)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static TicketFilterResolution WorkspaceFailure(string error)
|
||||
{
|
||||
return new TicketFilterResolution
|
||||
{
|
||||
Success = false,
|
||||
Error = error
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user