diff --git a/Docs/LiveHealthChecks-UI-ViewHealthCheck.jpg b/Docs/LiveHealthChecks-UI-ViewHealthCheck.jpg new file mode 100644 index 0000000..0327985 Binary files /dev/null and b/Docs/LiveHealthChecks-UI-ViewHealthCheck.jpg differ diff --git a/Docs/README_LiveHealthChecks.UI.md b/Docs/README_LiveHealthChecks.UI.md index 43f3b47..2bd9e43 100644 --- a/Docs/README_LiveHealthChecks.UI.md +++ b/Docs/README_LiveHealthChecks.UI.md @@ -52,4 +52,10 @@ received from the Server. Also, you can search this data in the Search popup (click on Search button on Dashboard). -![**Search**](/Docs/LiveHealthChecks-UI-Search.jpg) \ No newline at end of file +![**Search**](/Docs/LiveHealthChecks-UI-Search.jpg) + +You can click on a row in the table and a **View Health Check** popup comes up. + +This has all the details of the Health Check, including the Health Report JSON. + +![**View Health Check**](/Docs/LiveHealthChecks-UI-ViewHealthCheck.jpg) \ No newline at end of file diff --git a/LiveHealthChecks.UI/Components/ApiWidget.razor b/LiveHealthChecks.UI/Components/ApiWidget.razor index 2fdf0f6..3410f08 100644 --- a/LiveHealthChecks.UI/Components/ApiWidget.razor +++ b/LiveHealthChecks.UI/Components/ApiWidget.razor @@ -145,9 +145,9 @@ public Dictionary HealthChecks = new Dictionary(); - public List HealthChecksTemp = new List(); + public List HealthChecksTemp = new List(); - public List LastHealthChecks = new List(); + public List LastHealthChecks = new List(); ChartOptions ChartOptions = new ChartOptions(); @@ -183,10 +183,12 @@ { try { - dynamic jsonReport = JsonSerializer.Deserialize(report); + var deserializedHealthReport = JsonSerializer.Deserialize(report); + + // dynamic jsonReport = JsonSerializer.Deserialize(report); ////Generate random Status - //var td = int.Parse(jsonReport["TotalDuration"].ToString().Replace(":", "").Replace(".", "")); + //var td = int.Parse(deserializedHealthReport.TotalDuration?.ToString().Replace(":", "").Replace(".", "")); //Random = new Random(td); @@ -195,9 +197,11 @@ //var random = new Random(seed); //var num = random.Next(1, 100); - //jsonReport["Status"] = num % 2 == 0 ? 2 : 1; + ////jsonReport["Status"] = num % 2 == 0 ? 2 : 1; + + //deserializedHealthReport.Status = num % 2 == 0 ? Status.Healthy : Status.Unhealthy; - List healthChecksTemp; + List healthChecksTemp; if (await Repository.ContainKeyAsync(model.ReceiveMethod)) { @@ -205,14 +209,14 @@ } else { - healthChecksTemp = new List() { }; + healthChecksTemp = new List() { }; } var healthy = healthChecksTemp.Count(x => x.Status == HealthStatus.Healthy); var unHealthy = healthChecksTemp.Count(x => x.Status == HealthStatus.Unhealthy); double healthStatus; - if ((int)jsonReport["Status"] == 2) + if (deserializedHealthReport.Status == Status.Healthy) { healthy = healthy + 1; healthStatus = HealthStatus.Healthy; @@ -225,10 +229,10 @@ LastHealthCheck = "red"; } - healthChecksTemp.Add(new HealthReport { + healthChecksTemp.Add(new HealthCheck { Api = model.ApiName, ReceiveMethod = model.ReceiveMethod, - Report = report, + Report = deserializedHealthReport, ReceiveTimeStamp = DateTime.UtcNow, Status = healthStatus }); diff --git a/LiveHealthChecks.UI/Components/SearchWidget.razor b/LiveHealthChecks.UI/Components/SearchWidget.razor index 73ed1ed..0f11e8d 100644 --- a/LiveHealthChecks.UI/Components/SearchWidget.razor +++ b/LiveHealthChecks.UI/Components/SearchWidget.razor @@ -2,11 +2,12 @@ @using MudBlazor.Services @using LiveHealthChecks.UI.Models @using LiveHealthChecks.UI.Repository +@inject IDialogService DialogService @inject IMyHealthChecksRepository Repository @if(DashboardSettings != null && HealthChecks != null) { - + Health Reports @@ -74,16 +75,16 @@ } } - private List? HealthChecksView { get; set; } = new List(); + private List? HealthChecksView { get; set; } = new List(); - private List? HealthChecks { get; set; } = new List(); + private List? HealthChecks { get; set; } = new List(); private bool dense = false; private bool hover = true; private bool striped = false; private bool bordered = false; - private HealthReport? selectedItem1 = null; - private HashSet selectedItems = new HashSet(); + private HealthCheck? selectedItem1 = null; + private HashSet selectedItems = new HashSet(); protected override async Task OnInitializedAsync() { @@ -101,7 +102,7 @@ { if (DashboardSettings != null) { - HealthChecks = new List(); + HealthChecks = new List(); foreach(var api in DashboardSettings.Apis) { @@ -110,10 +111,31 @@ } } - private bool FilterFunc(HealthReport element) + private void HealthCheckSelected(TableRowClickEventArgs e) { - Func healthStatus = element => SelectedHealthStatus == "-1" ? true : element.Status == double.Parse(SelectedHealthStatus); - Func receiveTimestamp = (element, hours) => element.ReceiveTimeStamp >= DateTime.UtcNow.AddHours(hours); + OpenDialog(e.Item); + } + + private void OpenDialog(HealthCheck healthReport) + { + var options = new DialogOptions + { + CloseOnEscapeKey = true, + MaxWidth = MaxWidth.ExtraSmall, + FullWidth = true, + Position = DialogPosition.Center, + CloseButton = true, + }; + var parameters = new DialogParameters(); + parameters.Add(x => x.HealthCheck, healthReport); + + DialogService.Show("View Health Check", parameters, options); + } + + private bool FilterFunc(HealthCheck element) + { + Func healthStatus = element => SelectedHealthStatus == "-1" ? true : element.Status == double.Parse(SelectedHealthStatus); + Func receiveTimestamp = (element, hours) => element.ReceiveTimeStamp >= DateTime.UtcNow.AddHours(hours); switch(SelectedPeriod) { diff --git a/LiveHealthChecks.UI/Components/View.razor b/LiveHealthChecks.UI/Components/View.razor new file mode 100644 index 0000000..3529b4f --- /dev/null +++ b/LiveHealthChecks.UI/Components/View.razor @@ -0,0 +1,85 @@ +@using System.Text.Json +@using MudBlazor +@using LiveHealthChecks.UI.Models + +@if (HealthCheck != null) +{ +
+
+
+ Received Time +
+
+ Status +
+
+
+
+ @($"{HealthCheck?.ReceiveTimeStamp?.ToLocalTime().ToLongDateString()} - {HealthCheck?.ReceiveTimeStamp?.ToLocalTime().ToLongTimeString()}") +
+
+ @if(HealthCheck?.Status == HealthStatus.Healthy) + { + + } + else + { + + } +
+
+
+
+ Api +
+
+
+
+
+
+
+ @HealthCheck?.Api +
+
+
+
+
+
+
+ Health Report +
+
+
+
+ @if(@HealthCheck?.Report != null) + { +
@JsonPrettify(HealthCheck?.Report)
+ } +
+
+
+} + +@code { + [Parameter] + public HealthCheck? HealthCheck { get; set; } + + private string JsonPrettify(HealthReport healthReport) + { + if (healthReport == null) + { + return string.Empty; + } + + var transformedHealthReport = new TransformedHealthReport + { + Status = healthReport.Status.ToString(), + Results = healthReport.Entries?.ToDictionary(x => x.Key, y => y.Value.ToString()) + }; + + var transformedJson = JsonSerializer.Serialize(transformedHealthReport, + new JsonSerializerOptions { WriteIndented = true }); + + return transformedJson; + } +} diff --git a/LiveHealthChecks.UI/Components/ViewDialog.razor b/LiveHealthChecks.UI/Components/ViewDialog.razor new file mode 100644 index 0000000..546cb46 --- /dev/null +++ b/LiveHealthChecks.UI/Components/ViewDialog.razor @@ -0,0 +1,22 @@ +@using MudBlazor +@using LiveHealthChecks.UI.Models + + + + @if (HealthCheck != null) + { +
+ +
+ } +
+ + +
+ +@code { + [CascadingParameter] MudDialogInstance MudDialog { get; set; } + [Parameter] public HealthCheck? HealthCheck { get; set; } +} diff --git a/LiveHealthChecks.UI/Models/Models.cs b/LiveHealthChecks.UI/Models/Models.cs index 7454550..efc4438 100644 --- a/LiveHealthChecks.UI/Models/Models.cs +++ b/LiveHealthChecks.UI/Models/Models.cs @@ -1,11 +1,11 @@ namespace LiveHealthChecks.UI.Models { - public class HealthReport + public class HealthCheck { public string Api { get; set; } = string.Empty; public string ReceiveMethod { get; set; } = string.Empty; public DateTime? ReceiveTimeStamp { get; set; } = null; - public string Report { get; set; } = string.Empty; + public HealthReport? Report { get; set; } public double Status { get; set; } } @@ -15,4 +15,24 @@ public struct HealthStatus public const double Healthy = 2.00; } + + public class HealthReport + { + public Status Status { get; set; } + public string? TotalDuration { get; set; } + public Dictionary? Entries { get; set; } + } + + public enum Status + { + Unhealthy, + Degraded, + Healthy + } + + public class TransformedHealthReport + { + public string? Status { get; set; } + public Dictionary? Results { get; set; } + } } diff --git a/LiveHealthChecks.UI/Repository/MyHealthChecksRepository.cs b/LiveHealthChecks.UI/Repository/MyHealthChecksRepository.cs index c17a631..ed64deb 100644 --- a/LiveHealthChecks.UI/Repository/MyHealthChecksRepository.cs +++ b/LiveHealthChecks.UI/Repository/MyHealthChecksRepository.cs @@ -6,8 +6,8 @@ namespace LiveHealthChecks.UI.Repository public interface IMyHealthChecksRepository { ValueTask ContainKeyAsync(string key, CancellationToken cancellationToken = default); - ValueTask> GetHealthChecksDataAsync(string receiveMethod, CancellationToken cancellationToken = default); - ValueTask SetHealthChecksDataAsync(string receiveMethod, List data, CancellationToken cancellationToken = default); + ValueTask> GetHealthChecksDataAsync(string receiveMethod, CancellationToken cancellationToken = default); + ValueTask SetHealthChecksDataAsync(string receiveMethod, List data, CancellationToken cancellationToken = default); } @@ -25,12 +25,12 @@ public async ValueTask ContainKeyAsync(string key, CancellationToken cance return await _localStorageService.ContainKeyAsync(key, cancellationToken); } - public async ValueTask> GetHealthChecksDataAsync(string receiveMethod, CancellationToken cancellationToken = default) + public async ValueTask> GetHealthChecksDataAsync(string receiveMethod, CancellationToken cancellationToken = default) { - return await _localStorageService.GetItemAsync>(receiveMethod, cancellationToken); + return await _localStorageService.GetItemAsync>(receiveMethod, cancellationToken); } - public async ValueTask SetHealthChecksDataAsync(string receiveMethod, List data, CancellationToken cancellationToken = default) + public async ValueTask SetHealthChecksDataAsync(string receiveMethod, List data, CancellationToken cancellationToken = default) { await _localStorageService.SetItemAsync(receiveMethod, data, cancellationToken); }