Add generic ticket activity filters
This commit is contained in:
@@ -2,8 +2,9 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Security.Principal;
|
||||
@@ -31,14 +32,17 @@ using static C4IT.Matrix42.WebClient.cM42WebClient;
|
||||
|
||||
namespace C4IT.DataHistoryProvider
|
||||
{
|
||||
public class cDataHistoryCollectorM42Wpm : cDataHistoryCollectorModule, ISearchResultRelationProvider
|
||||
public class cDataHistoryCollectorM42Wpm : cDataHistoryCollectorModule, ISearchResultRelationProvider, ITicketActivityProvider
|
||||
{
|
||||
public const string constConnectorName = "Matrix42 WPM conntector";
|
||||
public const string constLicenseId = "9CE1A6BE-6A0C-4A27-94A5-44AB997B8E62";
|
||||
|
||||
private const string constUrlIsAlive = "m42Services/api/c4itf4sdwebapi/isalive";
|
||||
private const string constUrlGetTickets = "m42Services/api/c4itf4sdwebapi/getticketlist?sid={0}&hours={1}&queueoption={2}";
|
||||
private const string constUrlGetTicketsQueuesParam = "&queues={0}";
|
||||
private const string constUrlGetTickets = "m42Services/api/c4itf4sdwebapi/getticketlist?sid={0}&hours={1}&queueoption={2}";
|
||||
private const string constUrlGetTicketsQueuesParam = "&queues={0}";
|
||||
private const string constUrlGetTicketsV2 = "m42Services/api/c4itf4sdwebapi/v2/getticketlist";
|
||||
private const string constUrlGetTicketOverviewCountsV2 = "m42Services/api/c4itf4sdwebapi/v2/getticketoverviewcounts";
|
||||
private const string constUrlGetTicketOverviewRelationsV2 = "m42Services/api/c4itf4sdwebapi/v2/getticketoverviewrelations";
|
||||
private const string constUrlGetTicketDetails = "m42Services/api/c4itf4sdwebapi/getticketdetails?objectid={0}";
|
||||
private const string constUrlGetTicketHistory = "m42Services/api/c4itf4sdwebapi/gettickethistory?objectid={0}";
|
||||
private const string constUrlGetCreateTicket = "m42Services/api/c4itf4sdwebapi/getdirectlinkcreateticket?sid={0}{1}";
|
||||
@@ -687,24 +691,33 @@ namespace C4IT.DataHistoryProvider
|
||||
if (!await CheckOnline())
|
||||
return null;
|
||||
|
||||
var objSid = new SecurityIdentifier(UserId.sid, 0);
|
||||
var strSid = objSid.ToString();
|
||||
var queueOption = (int)Collector.InfrastructureConfig.M42Wpm.ActivityQueueFilterOption;
|
||||
var strUrl = string.Format(constUrlGetTickets,
|
||||
strSid,
|
||||
Collector.InfrastructureConfig.M42Wpm.ClosedTicketHistory,
|
||||
queueOption);
|
||||
|
||||
if (queueOption != 0)
|
||||
{
|
||||
var encodedQueues = GetEncodedActivityQueues();
|
||||
if (!string.IsNullOrWhiteSpace(encodedQueues))
|
||||
strUrl += string.Format(constUrlGetTicketsQueuesParam, encodedQueues);
|
||||
}
|
||||
|
||||
var _wc = await GetWebClient(requestInfo, Token);
|
||||
var res = await _wc.HttpEnh.GetAsync(strUrl);
|
||||
if (res.IsSuccessStatusCode)
|
||||
var objSid = new SecurityIdentifier(UserId.sid, 0);
|
||||
var strSid = objSid.ToString();
|
||||
var _wc = await GetWebClient(requestInfo, Token);
|
||||
var request = new cTicketActivityListRequest
|
||||
{
|
||||
Sid = strSid,
|
||||
Hours = Collector.InfrastructureConfig.M42Wpm.ClosedTicketHistory,
|
||||
Filters = GetActivityFilters()
|
||||
};
|
||||
var res = await PostJsonAsync(_wc, constUrlGetTicketsV2, request, Token);
|
||||
if (res?.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
if (!TryGetLegacyQueueFilter(out var queueOption, out var encodedQueues))
|
||||
{
|
||||
LogEntry("The activity filter policy requires the v2 ticket API; legacy fallback was rejected (fail closed).", LogLevels.Warning);
|
||||
return null;
|
||||
}
|
||||
var strUrl = string.Format(
|
||||
constUrlGetTickets,
|
||||
strSid,
|
||||
Collector.InfrastructureConfig.M42Wpm.ClosedTicketHistory,
|
||||
queueOption);
|
||||
if (!string.IsNullOrWhiteSpace(encodedQueues))
|
||||
strUrl += string.Format(constUrlGetTicketsQueuesParam, encodedQueues);
|
||||
res = await _wc.HttpEnh.GetAsync(strUrl);
|
||||
}
|
||||
if (res?.IsSuccessStatusCode == true)
|
||||
{
|
||||
var strJson = await res.Content.ReadAsStringAsync();
|
||||
if (!string.IsNullOrEmpty(strJson))
|
||||
@@ -1093,12 +1106,76 @@ namespace C4IT.DataHistoryProvider
|
||||
return filtered;
|
||||
}
|
||||
|
||||
private int GetActivityQueueOption()
|
||||
{
|
||||
return (int)Collector.InfrastructureConfig.M42Wpm.ActivityQueueFilterOption;
|
||||
}
|
||||
|
||||
private string GetEncodedActivityQueues()
|
||||
private int GetActivityQueueOption()
|
||||
{
|
||||
return (int)Collector.InfrastructureConfig.M42Wpm.ActivityQueueFilterOption;
|
||||
}
|
||||
|
||||
private cActivityFilterPolicy GetActivityFilters()
|
||||
{
|
||||
return Collector?.GetGlobalConfig()?.TicketConfiguration?.ActivityFilters
|
||||
?? new cActivityFilterPolicy();
|
||||
}
|
||||
|
||||
private bool TryGetLegacyQueueFilter(out int queueOption, out string encodedQueues)
|
||||
{
|
||||
queueOption = 0;
|
||||
encodedQueues = null;
|
||||
var activeFilters = (GetActivityFilters()?.Filters ?? new List<cActivityFilter>())
|
||||
.Where(entry => entry?.Enabled == true)
|
||||
.ToList();
|
||||
if (activeFilters.Count == 0)
|
||||
return true;
|
||||
if (activeFilters.Count != 1)
|
||||
return false;
|
||||
|
||||
var activityFilter = activeFilters[0];
|
||||
if (!string.Equals(activityFilter.Provider, "Matrix42", StringComparison.OrdinalIgnoreCase)
|
||||
|| !string.Equals(activityFilter.Field, "Queue", StringComparison.OrdinalIgnoreCase)
|
||||
|| activityFilter.Match != enumActivityFilterMatch.include
|
||||
|| activityFilter.EmptyHandling == enumActivityFilterEmptyHandling.only)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
queueOption = activityFilter.EmptyHandling == enumActivityFilterEmptyHandling.include ? 1 : 2;
|
||||
var values = (activityFilter.Values ?? new List<cActivityFilterValue>())
|
||||
.Where(value => value != null)
|
||||
.Select(value =>
|
||||
{
|
||||
if (Guid.TryParse(value.ID, out var id) && id != Guid.Empty)
|
||||
return ":" + HttpUtility.UrlEncode(id.ToString("D"));
|
||||
return string.IsNullOrWhiteSpace(value.Name)
|
||||
? null
|
||||
: HttpUtility.UrlEncode(value.Name.Trim());
|
||||
})
|
||||
.Where(value => !string.IsNullOrWhiteSpace(value))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
if (values.Count == 0)
|
||||
return false;
|
||||
|
||||
encodedQueues = string.Join("|", values);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Task<HttpResponseMessage> PostJsonAsync(
|
||||
cM42WebClient webClient,
|
||||
string url,
|
||||
object request,
|
||||
CancellationToken token)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(
|
||||
request,
|
||||
Formatting.None,
|
||||
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||
return webClient.HttpEnh.PostAsync(
|
||||
url,
|
||||
new StringContent(json, Encoding.UTF8, "application/json"),
|
||||
token);
|
||||
}
|
||||
|
||||
private string GetEncodedActivityQueues()
|
||||
{
|
||||
var queues = Collector.InfrastructureConfig.M42Wpm.ActivityQueues;
|
||||
if (queues == null || queues.Count == 0)
|
||||
@@ -1140,28 +1217,36 @@ namespace C4IT.DataHistoryProvider
|
||||
string scope,
|
||||
IReadOnlyCollection<string> requestedKeys,
|
||||
cF4sdWebRequestInfo requestInfo,
|
||||
CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var urlBuilder = new StringBuilder(constUrlGetTicketOverviewCounts);
|
||||
urlBuilder.Append("?sid=").Append(HttpUtility.UrlEncode(sid));
|
||||
urlBuilder.Append("&scope=").Append(HttpUtility.UrlEncode(scope ?? string.Empty));
|
||||
|
||||
if (requestedKeys != null && requestedKeys.Count > 0)
|
||||
{
|
||||
urlBuilder.Append("&keys=").Append(HttpUtility.UrlEncode(string.Join(",", requestedKeys)));
|
||||
}
|
||||
|
||||
var queueOption = GetActivityQueueOption();
|
||||
var encodedQueues = queueOption != 0 ? GetEncodedActivityQueues() : null;
|
||||
AppendQueueFilterQuery(urlBuilder, queueOption, encodedQueues);
|
||||
|
||||
var url = urlBuilder.ToString();
|
||||
var wc = await GetWebClient(requestInfo, token);
|
||||
var res = await wc.HttpEnh.GetAsync(url, token);
|
||||
if (token.IsCancellationRequested)
|
||||
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var wc = await GetWebClient(requestInfo, token);
|
||||
var request = new cTicketOverviewFilterRequest
|
||||
{
|
||||
Sid = sid,
|
||||
Scope = scope,
|
||||
Keys = requestedKeys?.ToList() ?? new List<string>(),
|
||||
Filters = GetActivityFilters()
|
||||
};
|
||||
var res = await PostJsonAsync(wc, constUrlGetTicketOverviewCountsV2, request, token);
|
||||
if (res?.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
if (!TryGetLegacyQueueFilter(out var queueOption, out var encodedQueues))
|
||||
{
|
||||
LogEntry("The activity filter policy requires the v2 overview API; legacy fallback was rejected (fail closed).", LogLevels.Warning);
|
||||
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
var urlBuilder = new StringBuilder(constUrlGetTicketOverviewCounts);
|
||||
urlBuilder.Append("?sid=").Append(HttpUtility.UrlEncode(sid));
|
||||
urlBuilder.Append("&scope=").Append(HttpUtility.UrlEncode(scope ?? string.Empty));
|
||||
if (requestedKeys != null && requestedKeys.Count > 0)
|
||||
urlBuilder.Append("&keys=").Append(HttpUtility.UrlEncode(string.Join(",", requestedKeys)));
|
||||
AppendQueueFilterQuery(urlBuilder, queueOption, encodedQueues);
|
||||
res = await wc.HttpEnh.GetAsync(urlBuilder.ToString(), token);
|
||||
}
|
||||
if (token.IsCancellationRequested)
|
||||
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (res?.IsSuccessStatusCode == true)
|
||||
{
|
||||
@@ -1227,64 +1312,12 @@ namespace C4IT.DataHistoryProvider
|
||||
if (string.IsNullOrWhiteSpace(sid))
|
||||
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var normalizedKeys = (keys ?? Enumerable.Empty<string>())
|
||||
.Where(k => !string.IsNullOrWhiteSpace(k))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
var now = DateTime.UtcNow;
|
||||
var ttl = GetTicketOverviewCacheDuration(useRoleScope);
|
||||
|
||||
if (!useRoleScope)
|
||||
{
|
||||
// process thr personal scope
|
||||
if (TryGetTicketOverviewPersonalCache(sid, now, out var cachedPersonal))
|
||||
return FilterTicketOverviewCounts(cachedPersonal, normalizedKeys);
|
||||
|
||||
var counts = await FetchTicketOverviewCountsAsync(sid, "personal", normalizedKeys, requestInfo, Token);
|
||||
if (Token.IsCancellationRequested)
|
||||
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
SetTicketOverviewPersonalCache(sid, counts, now.Add(ttl));
|
||||
return FilterTicketOverviewCounts(counts, normalizedKeys);
|
||||
}
|
||||
|
||||
// process the role scope
|
||||
var roleIds = await GetTicketOverviewRoleIdsAsync(sid, now, ttl, Token);
|
||||
if (roleIds == null || roleIds.Count == 0)
|
||||
return new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var countsByRole = new Dictionary<Guid, Dictionary<string, int>>();
|
||||
var missingRoles = new List<Guid>();
|
||||
|
||||
foreach (var roleId in roleIds)
|
||||
{
|
||||
if (TryGetTicketOverviewRoleCache(roleId, now, out var cachedRole))
|
||||
{
|
||||
countsByRole[roleId] = cachedRole;
|
||||
}
|
||||
else
|
||||
{
|
||||
missingRoles.Add(roleId);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingRoles.Count > 0)
|
||||
{
|
||||
var fetched = await FetchTicketOverviewCountsByRolesAsync(missingRoles, normalizedKeys, requestInfo, LogDeep + 1, Token);
|
||||
var expiresAt = DateTime.UtcNow.Add(ttl);
|
||||
|
||||
foreach (var roleId in missingRoles)
|
||||
{
|
||||
if (!fetched.TryGetValue(roleId, out var roleCounts))
|
||||
roleCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
SetTicketOverviewRoleCache(roleId, roleCounts, expiresAt);
|
||||
countsByRole[roleId] = roleCounts;
|
||||
}
|
||||
}
|
||||
|
||||
var summed = SumTicketOverviewCounts(countsByRole.Values);
|
||||
return FilterTicketOverviewCounts(summed, normalizedKeys);
|
||||
var normalizedKeys = (keys ?? Enumerable.Empty<string>())
|
||||
.Where(k => !string.IsNullOrWhiteSpace(k))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
var scope = useRoleScope ? "role" : "personal";
|
||||
return await FetchTicketOverviewCountsAsync(sid, scope, normalizedKeys, requestInfo, Token);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
@@ -1314,26 +1347,37 @@ namespace C4IT.DataHistoryProvider
|
||||
return new List<cF4sdApiSearchResultRelation>();
|
||||
|
||||
var sid = requestInfo?.userInfo?.AdSid;
|
||||
if (string.IsNullOrWhiteSpace(sid))
|
||||
return new List<cF4sdApiSearchResultRelation>();
|
||||
|
||||
var scope = useRoleScope ? "role" : "personal";
|
||||
var urlBuilder = new StringBuilder(constUrlGetTicketOverviewRelations);
|
||||
urlBuilder.Append("?sid=").Append(HttpUtility.UrlEncode(sid));
|
||||
urlBuilder.Append("&scope=").Append(HttpUtility.UrlEncode(scope));
|
||||
urlBuilder.Append("&key=").Append(HttpUtility.UrlEncode(key));
|
||||
urlBuilder.Append("&count=").Append(Math.Max(0, count));
|
||||
|
||||
var queueOption = GetActivityQueueOption();
|
||||
var encodedQueues = queueOption != 0 ? GetEncodedActivityQueues() : null;
|
||||
AppendQueueFilterQuery(urlBuilder, queueOption, encodedQueues);
|
||||
|
||||
var url = urlBuilder.ToString();
|
||||
|
||||
var wc = await GetWebClient(requestInfo, Token);
|
||||
var res = await wc.HttpEnh.GetAsync(url, Token);
|
||||
if (Token.IsCancellationRequested)
|
||||
return new List<cF4sdApiSearchResultRelation>();
|
||||
if (string.IsNullOrWhiteSpace(sid))
|
||||
return new List<cF4sdApiSearchResultRelation>();
|
||||
|
||||
var scope = useRoleScope ? "role" : "personal";
|
||||
var wc = await GetWebClient(requestInfo, Token);
|
||||
var request = new cTicketOverviewFilterRequest
|
||||
{
|
||||
Sid = sid,
|
||||
Scope = scope,
|
||||
Key = key,
|
||||
Count = Math.Max(0, count),
|
||||
Filters = GetActivityFilters()
|
||||
};
|
||||
var res = await PostJsonAsync(wc, constUrlGetTicketOverviewRelationsV2, request, Token);
|
||||
if (res?.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
if (!TryGetLegacyQueueFilter(out var queueOption, out var encodedQueues))
|
||||
{
|
||||
LogEntry("The activity filter policy requires the v2 relation API; legacy fallback was rejected (fail closed).", LogLevels.Warning);
|
||||
return new List<cF4sdApiSearchResultRelation>();
|
||||
}
|
||||
var urlBuilder = new StringBuilder(constUrlGetTicketOverviewRelations);
|
||||
urlBuilder.Append("?sid=").Append(HttpUtility.UrlEncode(sid));
|
||||
urlBuilder.Append("&scope=").Append(HttpUtility.UrlEncode(scope));
|
||||
urlBuilder.Append("&key=").Append(HttpUtility.UrlEncode(key));
|
||||
urlBuilder.Append("&count=").Append(Math.Max(0, count));
|
||||
AppendQueueFilterQuery(urlBuilder, queueOption, encodedQueues);
|
||||
res = await wc.HttpEnh.GetAsync(urlBuilder.ToString(), Token);
|
||||
}
|
||||
if (Token.IsCancellationRequested)
|
||||
return new List<cF4sdApiSearchResultRelation>();
|
||||
|
||||
if (res?.IsSuccessStatusCode == true)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user