using C4IT.FASD.Base; using C4IT.FASD.Cockpit.Communication; using C4IT.Logging; using FasdDesktopUi.Basics.CustomEvents; using FasdDesktopUi.Basics.Models; using FasdDesktopUi.Pages.DetailsPage.Models; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using static C4IT.Logging.cLogManager; namespace FasdDesktopUi.Basics.Services.SupportCase { /// /// Used for processing raw data of a for the UI in a certain way. /// internal class SupportCaseProcessor { private ISupportCase _supportCase; public cSupportCaseDataProvider SupportCaseDataProviderArtifact { get => _supportCase?.SupportCaseDataProviderArtifact; } private readonly Dictionary _detailsPageDataCache = new Dictionary(); private readonly Dictionary> _namedParameterCache = new Dictionary>(); internal void SetSupportCase(ISupportCase supportCase) { if (_supportCase != null) { _supportCase.CaseRelationsAdded -= HandleSupportCaseRelationsAdded; _supportCase.SupportCaseDataCacheHasChanged -= HandleSupportCaseDataCacheHasChanged; } _supportCase = supportCase; _supportCase.CaseRelationsAdded += HandleSupportCaseRelationsAdded; _supportCase.SupportCaseDataCacheHasChanged += HandleSupportCaseDataCacheHasChanged; } private void HandleSupportCaseRelationsAdded(object sender, RelationEventArgs e) => AvailableCaseRelationsAdded?.Invoke(this, e); private async void HandleSupportCaseDataCacheHasChanged(object sender, SupportCaseDataEventArgs e) { bool isArtifactShowingCorrectHealthCard = SupportCaseDataProviderArtifact.HealthCardDataHelper.SelectedHealthCard == GetHealthcardFor(e.Relation); if (!isArtifactShowingCorrectHealthCard) { // todo this can probably be removed, as soon as the last dependency of the SupportCaseDataProviderArtifact is gone. // till then the detailspageData gets overriden with the detailspageData of the new relation. // However, the removal shouldn't be much of a problem, due to the fact the Artifact also stores the raw data _detailsPageDataCache.Remove(e.Relation); return; } if (_detailsPageDataCache.TryGetValue(e.Relation, out var cachedData)) { cDetailsPageData detailData = _supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.DetailPage.GetDataWithoutHeading(); cachedData.WidgetData = detailData.WidgetData; cachedData.DataHistoryList = detailData.DataHistoryList; cachedData.MenuBarData = detailData.MenuBarData; cachedData.DataContainerCollectionList = detailData.DataContainerCollectionList; } else { _detailsPageDataCache[e.Relation] = await _supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.DetailPage.GetDataAsync(); } UpdateNamedParameters(e.Relation, e.DataTables); CaseDataChanged?.Invoke(this, e); } private void UpdateNamedParameters(cF4sdApiSearchResultRelation relation, IEnumerable dataTables) { try { if (!_namedParameterCache.ContainsKey(relation)) _namedParameterCache.Add(relation, new Dictionary()); var healthcard = GetHealthcardFor(relation); foreach (var namedParameter in cHealthCardPrerequisites.GetNamedParameters(healthcard).Values) { var table = dataTables.FirstOrDefault(t => t.Name == namedParameter.DatabaseInfo.ValueTable); if (table is null) continue; if (!table.Columns.TryGetValue(namedParameter.DatabaseInfo.ValueColumn, out var column)) continue; string value = cUtility.RawValueFormatter.GetDisplayValue(column.Values.FirstOrDefault(), namedParameter.Display); _namedParameterCache[relation][namedParameter.ParameterName] = value; } } catch (Exception ex) { LogException(ex); } } public async Task LoadSupportCaseDataAsync(cF4sdApiSearchResultRelation relation, IEnumerable tablesToLoad) { _ = Task.Run(async () => await _supportCase.LoadSupportCaseDataAsync(relation, tablesToLoad.Where(t => !t.Contains("-details-")))); if (!_detailsPageDataCache.TryGetValue(relation, out var detailsData)) { detailsData = await _supportCase.SupportCaseDataProviderArtifact.HealthCardDataHelper.DetailPage.GetDataAsync(); _detailsPageDataCache.Add(relation, detailsData); } CaseDataChanged?.Invoke(this, new SupportCaseDataEventArgs()); } public async Task UpdateLatestCaseDataFor(cF4sdApiSearchResultRelation relation) { try { int? agentUserId = relation.Identities.FirstOrDefault(i => i.Class == enumFasdInformationClass.User)?.agentId; int? agentDeviceId = relation.Identities.FirstOrDefault(i => i.Class == enumFasdInformationClass.Computer)?.agentId; await ActualizeDataAsync(agentUserId, agentDeviceId); _supportCase.InvalidateLatestCaseDataCacheFor(relation, out var invalidatedTables); _detailsPageDataCache.Remove(relation); await _supportCase.LoadSupportCaseDataAsync(relation, invalidatedTables.Select(t => t.Name)); } catch (Exception ex) { LogException(ex); } } private async Task ActualizeDataAsync(int? agentUserId, int? agentDeviceId) { var status = enumActualizeStatus.unknown; try { TimeSpan refreshDelay = TimeSpan.FromMilliseconds(500); const int maxPollCount = 20; if (!agentDeviceId.HasValue) { LogEntry("Coudldn't acutalize data. There was no valid AgentDeviceId found.", LogLevels.Error); return status; } var taskId = await cFasdCockpitCommunicationBase.Instance.ActualizeAgentData(agentDeviceId.Value, agentUserId); if (taskId == Guid.Empty) return enumActualizeStatus.failed; enumFasdInformationClass informationClass = agentUserId != null ? enumFasdInformationClass.User : enumFasdInformationClass.Computer; int pollCount = 0; do { status = await cFasdCockpitCommunicationBase.Instance.GetActualizeAgentDataStatus(taskId, informationClass); if (status == enumActualizeStatus.unknown) { pollCount++; if (pollCount >= maxPollCount) return status; await Task.Delay(refreshDelay); } } while (status == enumActualizeStatus.unknown); } catch (Exception E) { LogException(E); } return status; } internal ILookup GetCaseRelations() => _supportCase.GetCaseRelations(); internal cHealthCard GetHealthcardFor(cF4sdApiSearchResultRelation relation) { var availableHealthCards = cF4SDCockpitXmlConfig.Instance?.HealthCardConfig?.HealthCards?.Values; if (availableHealthCards is null || availableHealthCards.Count == 0) return null; return availableHealthCards .FirstOrDefault(hc => hc.InformationClasses.All(i => i == cF4sdIdentityEntry.GetFromSearchResult(relation.Type)) && HasCockpitUserRequiredRoles(hc.RequiredRoles)); } private static bool HasCockpitUserRequiredRoles(List requiredRoles) { try { if (requiredRoles is null || requiredRoles.Count == 0) return true; List roles = null; lock (cFasdCockpitCommunicationBase.CockpitUserInfoLock) { roles = cFasdCockpitCommunicationBase.CockpitUserInfo?.Roles; } if (roles is null || roles.Count == 0) return false; foreach (var requiredRole in requiredRoles) { if (roles.Contains(requiredRole, StringComparer.InvariantCultureIgnoreCase)) return true; } } catch (Exception E) { LogException(E); } return false; } public List> GetWidgetData(cF4sdApiSearchResultRelation relation) { List> widgetData = null; if (_detailsPageDataCache.TryGetValue(relation, out var detailsData)) widgetData = detailsData?.WidgetData; return widgetData ?? new List>(); } public cDetailsPageDataHistoryDataModel GetHistoryData(cF4sdApiSearchResultRelation relation) { cDetailsPageDataHistoryDataModel historyData = null; if (_detailsPageDataCache.TryGetValue(relation, out var detailsData)) historyData = detailsData?.DataHistoryList; return historyData ?? new cDetailsPageDataHistoryDataModel(); } public List GetContainerData(cF4sdApiSearchResultRelation relation) { List containerData = null; if (_detailsPageDataCache.TryGetValue(relation, out var detailsData)) containerData = detailsData?.DataContainerCollectionList; return containerData ?? new List(); } public List GetMenuBarData(cF4sdApiSearchResultRelation relation) { List menuData = null; if (_detailsPageDataCache.TryGetValue(relation, out var detailsData)) menuData = detailsData?.MenuBarData; return menuData ?? new List(); } /// /// Raised when newly available relations for a support case were added. /// public event EventHandler AvailableCaseRelationsAdded; /// /// Raised when the data set of a support case has changed. /// public event EventHandler CaseDataChanged; } }