From 258ca8a83786539a28262e56bbd6a75848577065 Mon Sep 17 00:00:00 2001 From: Meik Date: Wed, 1 Jul 2026 00:50:23 +0200 Subject: [PATCH] fix: load ticket history through journal portal api --- F4SDM42WebApi/F4SD - M42WebApi.csproj | 2 +- F4SDM42WebApi/F4SDHelperService.cs | 48 ++++++------ F4SDM42WebApi/F4SDM42WebApiController.cs | 95 ++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 22 deletions(-) diff --git a/F4SDM42WebApi/F4SD - M42WebApi.csproj b/F4SDM42WebApi/F4SD - M42WebApi.csproj index 3023793..e836f90 100644 --- a/F4SDM42WebApi/F4SD - M42WebApi.csproj +++ b/F4SDM42WebApi/F4SD - M42WebApi.csproj @@ -13,7 +13,7 @@ false 6673a25d-33d6-4072-91d9-abed4be1a966 C4ITF4SD*.* - 1.4.0.30 + 1.4.0.31 true diff --git a/F4SDM42WebApi/F4SDHelperService.cs b/F4SDM42WebApi/F4SDHelperService.cs index c6fcc86..1c84ee2 100644 --- a/F4SDM42WebApi/F4SDHelperService.cs +++ b/F4SDM42WebApi/F4SDHelperService.cs @@ -1408,29 +1408,9 @@ namespace C4IT.F4SD try { await Task.Delay(0); - List journalEntries = new List(); var entries = LoadJournalEntries(activityEOID); LogEntry($"{entries.Count} Journal entries found for ObjectID={activityEOID}", LogLevels.Debug); - for (int i = 0; i < entries.Count; i++) - { - Matrix42.Contracts.Platform.Data.JournalEntryInfo item = entries[i]; - var text = item.Text ?? string.Empty; - var createdDate = item.CreatedDate ?? DateTime.MinValue; - - LogEntry($"Journal entry {i + 1}/{entries.Count}: ID={item.Id}, CreatedDate={createdDate}, CreatedBy={item.Creator}, Header={item.Header}", LogLevels.Debug); - journalEntries.Add(new cTicketJournalItem() - { - JournalId = item.Id, - ActivityObjectId = activityEOID, - CreatedBy = item.Creator, - CreationDate = createdDate, - DescriptionHtml = text, - Description = string.IsNullOrEmpty(text) ? string.Empty : Matrix42.Common.Html.HtmlConverter.ConvertHtmlToPlainText(text), - Header = item.Header, - IsVisibleForUser = item.VisibleInPortal - }); - } - return journalEntries; + return CreateTicketJournalItems(activityEOID, entries); } catch (MissingMethodException E) { @@ -1447,6 +1427,32 @@ namespace C4IT.F4SD } } + internal static List CreateTicketJournalItems(Guid activityEOID, IReadOnlyList entries) + { + List journalEntries = new List(); + for (int i = 0; i < entries.Count; i++) + { + Matrix42.Contracts.Platform.Data.JournalEntryInfo item = entries[i]; + var text = item.Text ?? string.Empty; + var createdDate = item.CreatedDate ?? DateTime.MinValue; + + LogEntry($"Journal entry {i + 1}/{entries.Count}: ID={item.Id}, CreatedDate={createdDate}, CreatedBy={item.Creator}, Header={item.Header}", LogLevels.Debug); + journalEntries.Add(new cTicketJournalItem() + { + JournalId = item.Id, + ActivityObjectId = activityEOID, + CreatedBy = item.Creator, + CreationDate = createdDate, + DescriptionHtml = text, + Description = string.IsNullOrEmpty(text) ? string.Empty : Matrix42.Common.Html.HtmlConverter.ConvertHtmlToPlainText(text), + Header = item.Header, + IsVisibleForUser = item.VisibleInPortal + }); + } + + return journalEntries; + } + private static IReadOnlyList LoadJournalEntries(Guid activityEOID) { var journalService = F4SDM42WebApiController.defaultInstance.GetJournalServiceInstance(); diff --git a/F4SDM42WebApi/F4SDM42WebApiController.cs b/F4SDM42WebApi/F4SDM42WebApiController.cs index 0d72fe5..b48d35d 100644 --- a/F4SDM42WebApi/F4SDM42WebApiController.cs +++ b/F4SDM42WebApi/F4SDM42WebApiController.cs @@ -45,6 +45,10 @@ namespace C4IT.F4SD private readonly F4SDHelperService _f4stHelperService; private const string TeamsNotificationServiceTypeName = "Matrix42.Contracts.Platform.MsTeams.ITeamsNotificationService"; private const string StorageServiceTypeName = "Matrix42.StorageService.Contracts.IStorageService"; + private const int JournalStart = 0; + private const int JournalCount = 50; + private const int JournalTimeOffset = 120; + private const string JournalObjectLinkTemplate = "{2}"; public string BaseUrl => Request?.RequestUri == null ? string.Empty : $"{Request.RequestUri.Scheme}://{Request.RequestUri.Host}"; public string EndpointBaseUrl => $"{BaseUrl}/m42Services/api/c4itf4sdwebapi"; @@ -304,6 +308,13 @@ namespace C4IT.F4SD { try { + var journalPortalEntries = await TryGetJournalPortalEntries(objectId); + if (journalPortalEntries != null) + { + LogEntry($"{journalPortalEntries.Count} Journal entries loaded from Matrix42 JournalPortalController for ObjectID={objectId}", LogLevels.Debug); + return F4SDHelperService.CreateTicketJournalItems(objectId, journalPortalEntries); + } + return await _f4stHelperService.GetJournalEntries(objectId); } catch (MissingMethodException ex) @@ -312,6 +323,90 @@ namespace C4IT.F4SD } } + private async Task> TryGetJournalPortalEntries(Guid objectId) + { + if (Request?.RequestUri == null) + return null; + + foreach (var requestUri in BuildJournalPortalUris(objectId)) + { + try + { + using (var handler = new HttpClientHandler { UseDefaultCredentials = true, AutomaticDecompression = DecompressionMethods.All }) + using (var client = new HttpClient(handler)) + using (var request = new HttpRequestMessage(HttpMethod.Get, requestUri)) + { + client.Timeout = TimeSpan.FromSeconds(15); + CopyIncomingHeaders(request); + using (var response = await client.SendAsync(request)) + { + var responseText = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + { + LogEntry($"Matrix42 JournalPortalController returned {(int)response.StatusCode} {response.ReasonPhrase} for {requestUri}. {responseText}", LogLevels.Warning); + continue; + } + + var entries = DeserializeJournalPortalEntries(responseText); + if (entries != null) + return entries; + + LogEntry($"Matrix42 JournalPortalController response could not be deserialized for {requestUri}. {responseText}", LogLevels.Warning); + } + } + } + catch (Exception ex) + { + LogEntry($"Matrix42 JournalPortalController request failed for {requestUri}. {ex.GetType().FullName}: {ex.Message}", LogLevels.Warning); + } + } + + return null; + } + + private IEnumerable BuildJournalPortalUris(Guid objectId) + { + var query = $"objectId={Uri.EscapeDataString(objectId.ToString())}&start={JournalStart}&count={JournalCount}&objectLinkTemplate={Uri.EscapeDataString(JournalObjectLinkTemplate)}&timeOffset={JournalTimeOffset}"; + yield return new Uri($"{BaseUrl}/m42Services/api/journalPortal?{query}"); + yield return new Uri($"{BaseUrl}/api/journalPortal?{query}"); + } + + private void CopyIncomingHeaders(HttpRequestMessage request) + { + foreach (var header in Request.Headers) + { + if (string.Equals(header.Key, "Host", StringComparison.OrdinalIgnoreCase) + || string.Equals(header.Key, "Connection", StringComparison.OrdinalIgnoreCase) + || string.Equals(header.Key, "Accept-Encoding", StringComparison.OrdinalIgnoreCase)) + continue; + + request.Headers.TryAddWithoutValidation(header.Key, header.Value); + } + } + + private static Matrix42.Contracts.Platform.Data.JournalEntryInfo[] DeserializeJournalPortalEntries(string responseText) + { + try + { + return Newtonsoft.Json.JsonConvert.DeserializeObject(responseText); + } + catch + { + var token = Newtonsoft.Json.Linq.JToken.Parse(responseText); + if (token.Type == Newtonsoft.Json.Linq.JTokenType.Array) + return token.ToObject(); + + foreach (var propertyName in new[] { "result", "Result", "value", "Value" }) + { + var resultToken = token[propertyName]; + if (resultToken != null && resultToken.Type != Newtonsoft.Json.Linq.JTokenType.Null) + return resultToken.ToObject(); + } + + return null; + } + } + internal static List CreateMissingMethodDiagnostics(string stage, Guid objectId, MissingMethodException ex) { var details = BuildMissingMethodDiagnostic(stage, objectId, ex);