feat: support parallel legacy and sandbox extensions

This commit is contained in:
Meik
2026-07-13 15:09:21 +02:00
parent b8ad580ae5
commit 58dcc9f943
89 changed files with 10102 additions and 123 deletions

View File

@@ -7,7 +7,7 @@ using System.Net.Http;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Matrix42.Common;
using Matrix42.Contracts.Common.Security;
using Matrix42.Contracts.ServiceManagement.ServiceContracts;
@@ -18,14 +18,14 @@ using Matrix42.Services.Description.Contracts;
using Matrix42.WebApi.Contracts;
using Matrix42.WebApi.Contracts.OData;
using update4u.SPS.Utility.GlobalConfiguration;
using C4IT.F4SDM;
using C4IT.FASD.Base;
using C4IT.Logging;
using static C4IT.FASD.Base.cF4SDTicket;
using static C4IT.Logging.cLogManager;
namespace C4IT.F4SD
{
[RoutePrefix("api/C4ITF4SDWebApi")]
@@ -39,12 +39,12 @@ namespace C4IT.F4SD
//public readonly IFragmentService _fragmentService;
private readonly IDependencyResolver _resolver;
private readonly IEnumerationProvider _enumerationProvider;
private readonly F4SDHelperService _f4stHelperService;
public string BaseUrl => Request?.RequestUri == null ? string.Empty : $"{Request.RequestUri.Scheme}://{Request.RequestUri.Host}";
public string EndpointBaseUrl => $"{BaseUrl}/m42Services/api/c4itf4sdwebapi";
public F4SDM42WebApiController(IDependencyResolver resolver, IEnumerationProvider enumerationProvider)
{
_resolver = resolver ?? throw new ArgumentNullException(nameof(resolver));
@@ -53,12 +53,12 @@ namespace C4IT.F4SD
//_objectService = objectService;
//_fragmentService = fragmentService;
//_incidentService = Guard.NullArgument(incidentService, "incidentService");
_globalConfigurationProvider = GlobalConfigurationProvider.Instance;
_f4stHelperService = new F4SDHelperService();
EnsureInitialized();
}
}
private static readonly object initLock = new object();
private static void EnsureInitialized()
{
@@ -78,22 +78,22 @@ namespace C4IT.F4SD
}
}
catch { };
}
}
private T GetRequiredService<T>() where T : class
{
var service = _resolver.TryGet<T>();
if (service != null)
return service;
throw new InvalidOperationException($"Required Matrix42 service is not registered: {typeof(T).FullName}");
}
internal IJournalService GetJournalService()
{
return GetRequiredService<IJournalService>();
}
[Route("getDirectLinkCreateTicket"), HttpGet]
public async Task<F4SDHelperService.DirectLink> getDirectLinkCreateTicket([FromUri] string sid = "", [FromUri] string assetname = "")
{
@@ -110,8 +110,9 @@ namespace C4IT.F4SD
type = QueryValue(type, nameof(type));
return await _f4stHelperService.getDirectLinkF4SD(eoid, type) ?? string.Empty;
}
[Route("getTicketList"), HttpGet]
[Obsolete("Use getTicketListForUser with a Matrix42 user ID.")]
[Route("getTicketList"), HttpGet]
public async Task<List<cF4SDTicketSummary>> getTicketList(
[FromUri] string sid,
[FromUri] int hours,
@@ -124,7 +125,7 @@ namespace C4IT.F4SD
queueoption = QueryValue(queueoption, nameof(queueoption));
queues = QueryValue(queues, nameof(queues));
var decodedPairs = ParseQueues(queues);
// Nun weiterreichen an Service
return await _f4stHelperService.getTicketListByUser(
sid,
@@ -132,8 +133,27 @@ namespace C4IT.F4SD
queueoption,
decodedPairs
) ?? new List<cF4SDTicketSummary>();
}
}
[Route("getTicketListForUser"), HttpGet]
public async Task<List<cF4SDTicketSummary>> getTicketListForUser(
[FromUri] Guid userId,
[FromUri] int hours,
[FromUri] int queueoption = 0,
[FromUri] string queues = "")
{
userId = QueryValue(userId, nameof(userId));
hours = QueryValue(hours, nameof(hours));
queueoption = QueryValue(queueoption, nameof(queueoption));
queues = QueryValue(queues, nameof(queues));
return await _f4stHelperService.getTicketListByUser(
userId,
hours,
queueoption,
ParseQueues(queues)) ?? new List<cF4SDTicketSummary>();
}
[Route("getTicketDetails"), HttpGet]
public async Task<cF4SDTicket> getTicketDetails([FromUri] Guid objectId)
@@ -142,18 +162,19 @@ namespace C4IT.F4SD
var tickets = await _f4stHelperService.getTicketDetails(new List<Guid>() { objectId });
if (tickets?.Count > 0)
return tickets[0];
return new cF4SDTicket { TicketObjectId = objectId };
}
[Route("getTicketHistory"), HttpGet]
public async Task<List<cTicketJournalItem>> getTicketHistory([FromUri] Guid objectId)
{
objectId = QueryValue(objectId, nameof(objectId));
return await _f4stHelperService.GetJournalEntries(objectId) ?? new List<cTicketJournalItem>();
}
[Route("getTicketOverviewCounts"), HttpGet]
[Obsolete("Use getTicketOverviewCountsForUser with a Matrix42 user ID.")]
[Route("getTicketOverviewCounts"), HttpGet]
public async Task<F4SDHelperService.TicketOverviewCountsResult> getTicketOverviewCounts(
[FromUri] string sid,
[FromUri] string scope = "personal",
@@ -172,13 +193,42 @@ namespace C4IT.F4SD
.Select(key => key.Trim())
.Where(key => !string.IsNullOrWhiteSpace(key))
.ToList();
var decodedQueues = ParseQueues(queues);
return await _f4stHelperService.getTicketOverviewCounts(sid, scope, parsedKeys, queueoption, decodedQueues)
?? new F4SDHelperService.TicketOverviewCountsResult();
}
[Route("getTicketOverviewCountsByRoles"), HttpPost]
}
[Route("getTicketOverviewCountsForUser"), HttpGet]
public async Task<F4SDHelperService.TicketOverviewCountsResult> getTicketOverviewCountsForUser(
[FromUri] Guid userId,
[FromUri] string scope = "personal",
[FromUri] string keys = "",
[FromUri] int queueoption = 0,
[FromUri] string queues = "")
{
userId = QueryValue(userId, nameof(userId));
scope = QueryValue(scope, nameof(scope));
keys = QueryValue(keys, nameof(keys));
queueoption = QueryValue(queueoption, nameof(queueoption));
queues = QueryValue(queues, nameof(queues));
var parsedKeys = (keys ?? string.Empty)
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(key => key.Trim())
.Where(key => !string.IsNullOrWhiteSpace(key))
.ToList();
return await _f4stHelperService.getTicketOverviewCounts(
userId,
scope,
parsedKeys,
queueoption,
ParseQueues(queues)) ?? new F4SDHelperService.TicketOverviewCountsResult();
}
[Obsolete("Use getTicketOverviewCountsByRolesForUser with a Matrix42 user ID.")]
[Route("getTicketOverviewCountsByRoles"), HttpPost]
public async Task<F4SDHelperService.TicketOverviewCountsByRoleResult> getTicketOverviewCountsByRoles([FromBody] TicketOverviewCountsByRolesRequest request)
{
var parsedKeys = (request?.Keys ?? new List<string>())
@@ -186,12 +236,12 @@ namespace C4IT.F4SD
.Select(key => key.Trim())
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
var roleGuids = (request?.RoleGuids ?? new List<Guid>())
.Where(roleId => roleId != Guid.Empty)
.Distinct()
.ToList();
var decodedQueues = ParseQueues(request?.Queues ?? string.Empty);
return await _f4stHelperService.getTicketOverviewCountsByRoles(
request?.Sid,
@@ -200,9 +250,34 @@ namespace C4IT.F4SD
request?.QueueOption ?? 0,
decodedQueues
) ?? new F4SDHelperService.TicketOverviewCountsByRoleResult();
}
[Route("getTicketOverviewRelations"), HttpGet]
}
[Route("getTicketOverviewCountsByRolesForUser"), HttpPost]
public async Task<F4SDHelperService.TicketOverviewCountsByRoleResult> getTicketOverviewCountsByRolesForUser(
[FromBody] TicketOverviewCountsByRolesForUserRequest request)
{
var parsedKeys = (request?.Keys ?? new List<string>())
.Where(key => !string.IsNullOrWhiteSpace(key))
.Select(key => key.Trim())
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
var roleGuids = (request?.RoleGuids ?? new List<Guid>())
.Where(roleId => roleId != Guid.Empty)
.Distinct()
.ToList();
return await _f4stHelperService.getTicketOverviewCountsByRoles(
request?.userId ?? Guid.Empty,
roleGuids,
parsedKeys,
request?.QueueOption ?? 0,
ParseQueues(request?.Queues ?? string.Empty))
?? new F4SDHelperService.TicketOverviewCountsByRoleResult();
}
[Obsolete("Use getTicketOverviewRelationsForUser with a Matrix42 user ID.")]
[Route("getTicketOverviewRelations"), HttpGet]
public async Task<List<F4SDHelperService.TicketOverviewRelationDto>> getTicketOverviewRelations(
[FromUri] string sid,
[FromUri] string scope = "personal",
@@ -221,7 +296,32 @@ namespace C4IT.F4SD
var decodedQueues = ParseQueues(queues);
return await _f4stHelperService.getTicketOverviewRelations(sid, scope, key, count, queueoption, decodedQueues)
?? new List<F4SDHelperService.TicketOverviewRelationDto>();
}
}
[Route("getTicketOverviewRelationsForUser"), HttpGet]
public async Task<List<F4SDHelperService.TicketOverviewRelationDto>> getTicketOverviewRelationsForUser(
[FromUri] Guid userId,
[FromUri] string scope = "personal",
[FromUri] string key = "",
[FromUri] int count = 0,
[FromUri] int queueoption = 0,
[FromUri] string queues = "")
{
userId = QueryValue(userId, nameof(userId));
scope = QueryValue(scope, nameof(scope));
key = QueryValue(key, nameof(key));
count = QueryValue(count, nameof(count));
queueoption = QueryValue(queueoption, nameof(queueoption));
queues = QueryValue(queues, nameof(queues));
return await _f4stHelperService.getTicketOverviewRelations(
userId,
scope,
key,
count,
queueoption,
ParseQueues(queues)) ?? new List<F4SDHelperService.TicketOverviewRelationDto>();
}
/*
[Route("updateActivitySolution/{objectId}"), HttpPost]
public async Task<HttpResponseMessage> updateActivitySolution(Guid objectId, [FromBody] string SolutionHtml)
@@ -242,18 +342,18 @@ namespace C4IT.F4SD
group = QueryValue(group, nameof(group));
EntityEnumeration enumerationTemp = GetEnumeration(name, mode);
var vals = enumerationTemp.Values;
if (group > -1)
{
vals = vals.Where(row => !row.Extentions.TryGetValue("StateGroup", out var stateGroup) || (ConvertHelper.ParseInt(stateGroup, 0) == group)).ToArray();
}
string[] columns = new string[] { "position" };
foreach (var item in vals)
{
item.Extentions = item.Extentions.Where(x => columns.Contains(x.Key.ToLower())).ToDictionary(x => x.Key, x => x.Value);
}
EntityEnumeration enumeration = new EntityEnumeration
{
Name = enumerationTemp.Name,
@@ -262,26 +362,26 @@ namespace C4IT.F4SD
//CacheOutputAttribute.RegisterResponseEtag($"enum_{enumeration.Name}_{(int)mode}", $"{enumeration.Name}_{(int)mode}", cultureInvariant: false, userInvariant: true, val);
return enumeration;
}
[Route("getMyRoleMemberships"), HttpGet]
public async Task<object> getMyRoleMemberships()
{
var userId = GetCurrentUserId();
if (userId == Guid.Empty)
throw new UnauthorizedAccessException("Cannot determine the interactive Matrix42 user.");
var filter = AsqlHelper.BuildInCondition("ID", new Guid[] { userId });
return await _f4stHelperService.UserPermissionsInfo(filter) ?? new UserPermissionsInfo();
}
[HttpGet]
[Route("getRoleMemberships")]
public async Task<object> getRoleMemberships([FromUri] string sid = "", [FromUri] string upn = "", [FromUri] Guid? id = null)
public async Task<object> getRoleMemberships([FromUri] string sid = "", [FromUri] string upn = "", [FromUri] Guid? id = null)
{
sid = QueryValue(sid, nameof(sid));
upn = QueryValue(upn, nameof(upn));
id = QueryValue(id, nameof(id));
var filter = "";
if (id != null && id.Value != Guid.Empty)
{
filter = AsqlHelper.BuildInCondition("ID", new Guid[] { id.Value });
@@ -294,41 +394,58 @@ namespace C4IT.F4SD
{
filter = AsqlHelper.BuildInCondition("Accounts.T(SPSAccountClassAD).UserPrincipalName", new string[] { upn });
}
if (!string.IsNullOrEmpty(filter))
{
return await _f4stHelperService.UserPermissionsInfo(filter) ?? new UserPermissionsInfo();
}
return new UserPermissionsInfo();
}
public class TicketOverviewCountsByRolesRequest
return new UserPermissionsInfo();
}
[HttpGet]
[Route("getRoleMemberships/{userId}")]
public async Task<object> getRoleMembershipForUser([FromUri] Guid userId)
{
return await _f4stHelperService.UserPermissionsInfo(
AsqlHelper.BuildInCondition("ID", new[] { userId })) ?? new UserPermissionsInfo();
}
public class TicketOverviewCountsByRolesRequest
{
public string Sid { get; set; }
public List<Guid> RoleGuids { get; set; } = new List<Guid>();
public List<string> Keys { get; set; } = new List<string>();
public int? QueueOption { get; set; }
public string Queues { get; set; }
}
public string Queues { get; set; }
}
public class TicketOverviewCountsByRolesForUserRequest
{
public Guid userId { get; set; }
public List<Guid> RoleGuids { get; set; } = new List<Guid>();
public List<string> Keys { get; set; } = new List<string>();
public int? QueueOption { get; set; }
public string Queues { get; set; }
}
private EntityEnumeration GetEnumeration(string name, EntityEnumerationVisibilityMode mode)
{
name = name?.Trim();
var dataTable = _enumerationProvider.GetEnumeration(name, GetVisibilityFilter(mode));
if (dataTable == null)
throw new InvalidOperationException($"Enumeration '{name}' was not found.");
var valueColumn = FindColumn(dataTable, "Value") ?? FindNumericColumn(dataTable);
if (string.IsNullOrEmpty(valueColumn))
throw new InvalidOperationException($"Enumeration '{name}' does not contain a numeric value column.");
var displayColumn = FindColumn(dataTable, "DisplayString")
?? FindColumn(dataTable, "DisplayExpression")
?? FindColumn(dataTable, "Name")
?? valueColumn;
var hiddenColumn = FindColumn(dataTable, "Hidden");
var values = dataTable.Rows.Cast<DataRow>()
.Select(row => new EntityEnumerationValue
{
@@ -339,14 +456,14 @@ namespace C4IT.F4SD
.ToDictionary(column => column.ColumnName, column => row[column])
})
.ToArray();
return new EntityEnumeration
{
Name = name,
Values = values
};
}
private static bool? GetVisibilityFilter(EntityEnumerationVisibilityMode mode)
{
switch (mode)
@@ -359,14 +476,14 @@ namespace C4IT.F4SD
return null;
}
}
private static string FindColumn(DataTable dataTable, string columnName)
{
return dataTable.Columns.Cast<DataColumn>()
.FirstOrDefault(column => string.Equals(column.ColumnName, columnName, StringComparison.OrdinalIgnoreCase))
?.ColumnName;
}
private static string FindNumericColumn(DataTable dataTable)
{
var excludedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
@@ -375,12 +492,12 @@ namespace C4IT.F4SD
"Position",
"StateGroup"
};
return dataTable.Columns.Cast<DataColumn>()
.FirstOrDefault(column => !excludedNames.Contains(column.ColumnName) && IsNumericType(column.DataType))
?.ColumnName;
}
private static bool IsNumericType(Type type)
{
return type == typeof(byte)
@@ -392,7 +509,7 @@ namespace C4IT.F4SD
|| type == typeof(uint)
|| type == typeof(ulong);
}
private static Guid GetCurrentUserId()
{
var principal = Thread.CurrentPrincipal as IM42Principal;
@@ -400,39 +517,39 @@ namespace C4IT.F4SD
?? principal?.M42Identity?.UserFragmentID
?? Guid.Empty;
}
private string GetQueryValue(string name)
{
return Request?.GetQueryNameValuePairs()
.FirstOrDefault(pair => string.Equals(pair.Key, name, StringComparison.OrdinalIgnoreCase))
.Value;
}
private string QueryValue(string value, string name)
{
return GetQueryValue(name) ?? value ?? string.Empty;
}
private int QueryValue(int value, string name)
{
return int.TryParse(GetQueryValue(name), out var parsed) ? parsed : value;
}
private Guid QueryValue(Guid value, string name)
{
return Guid.TryParse(GetQueryValue(name), out var parsed) ? parsed : value;
}
private Guid? QueryValue(Guid? value, string name)
{
return Guid.TryParse(GetQueryValue(name), out var parsed) ? parsed : value;
}
private TEnum QueryValue<TEnum>(TEnum value, string name) where TEnum : struct
{
return Enum.TryParse<TEnum>(GetQueryValue(name), true, out var parsed) ? parsed : value;
}
private static List<cApiM42TicketQueueInfo> ParseQueues(string queues)
{
return (queues ?? string.Empty)
@@ -442,10 +559,10 @@ namespace C4IT.F4SD
var segments = part.Split(':');
if (segments.Length != 2)
return null;
var name = WebUtility.UrlDecode(segments[0]);
var idStr = WebUtility.UrlDecode(segments[1]);
return Guid.TryParse(idStr, out var guid)
? new cApiM42TicketQueueInfo { QueueName = name, QueueID = guid }
: null;
@@ -453,7 +570,7 @@ namespace C4IT.F4SD
.Where(q => q != null)
.ToList();
}
[Route("isAlive"), HttpGet]
public IHttpActionResult isAlive()
{
@@ -462,10 +579,10 @@ namespace C4IT.F4SD
RequestMessage = Request
};
response.Headers.ConnectionClose = true;
return ResponseMessage(response);
}
[Route("loglevel"), HttpGet]
public async Task<string> setDebugMode([FromUri] string debug = "0")
{
@@ -488,7 +605,7 @@ namespace C4IT.F4SD
LogMethodEnd(CM);
}
}
[Route("log"), HttpGet]
public IHttpActionResult getLog([FromUri] string download = "0", [FromUri] int count = 50, [FromUri] string filter = "")
{
@@ -500,7 +617,7 @@ namespace C4IT.F4SD
var response = _f4stHelperService.privGetLog(download, count, Request, filter);
if (response == null)
return new StatusCodeResult(HttpStatusCode.NoContent);
return new ResponseMessageResult(response);
}
catch (Exception E)
@@ -514,9 +631,9 @@ namespace C4IT.F4SD
public partial class F4SDM42LogsWebApiController : ApiController
{
private readonly F4SDHelperService _f4stHelperService;
public static bool IsInitialized { get; private set; } = false;
private static readonly object initLock = new object();
private static void EnsureInitialized()
{
@@ -537,15 +654,15 @@ namespace C4IT.F4SD
}
catch { };
}
public F4SDM42LogsWebApiController()
{
_f4stHelperService = new F4SDHelperService();
EnsureInitialized();
}
[Route(""), HttpGet]
public IEnumerable<cM42LogEntry> getLog2(ODataQueryOptions<cM42LogEntry> queryOptions)
{
@@ -559,26 +676,26 @@ namespace C4IT.F4SD
return Enumerable.Empty<cM42LogEntry>();
}
}
[Route("$count")]
[HttpGet]
public int Log2Count(ODataQueryOptions<cM42LogEntry> queryOptions)
{
return ApplyLogFilter(_f4stHelperService.privGetLog2(), queryOptions).Count();
}
[Route("{id}")]
[OperationType(OperationType.GetObject)]
public cM42LogEntry GetClass(int id)
{
return _f4stHelperService.privGetLog2(id) ?? new cM42LogEntry { LineNumber = id };
}
private static IEnumerable<cM42LogEntry> ApplyLogFilter(IEnumerable<cM42LogEntry> entries, ODataQueryOptions<cM42LogEntry> queryOptions)
{
var result = entries ?? Enumerable.Empty<cM42LogEntry>();
var filter = queryOptions?.Filter;
if (!string.IsNullOrWhiteSpace(filter))
{
result = result.Where(entry =>
@@ -586,16 +703,16 @@ namespace C4IT.F4SD
(entry.logLvl?.IndexOf(filter, StringComparison.OrdinalIgnoreCase) ?? -1) >= 0 ||
(entry.Theme?.IndexOf(filter, StringComparison.OrdinalIgnoreCase) ?? -1) >= 0);
}
return result;
}
}
public class cGetPropertyBody
{
public string TableName { get; set; }
public List<string> Columns { get; set; } = new List<string>();
public cGetPropertyBody() { }
}
}