Commit 331ec3c6 authored by Dmytro Bogatov's avatar Dmytro Bogatov 💕

Add discrepancy tabs.

parent bd3ab2f7
Pipeline #587 failed with stages
in 0 seconds
......@@ -43,6 +43,22 @@ namespace StatusMonitor.Shared.Models.Entities
}
public override string ToString() => ToStringWithTimeZone();
public string Description() {
switch (Type)
{
case DiscrepancyType.GapInData:
return "Occurs if difference in timestamps between two consecutive data points exceeds certain value.";
case DiscrepancyType.HighLoad:
return "Occurs if CPU load exceeds certain value for some number of consecutive data points.";
case DiscrepancyType.LowHealth:
return "Occurs if system health value drops below certain value for some number of consecutive health reports.";
case DiscrepancyType.PingFailedNTimes:
return "Occurs if ping fails certain number of consecutive readings.";
default:
return "Unknown type";
}
}
}
public enum DiscrepancyType
......
......@@ -33,6 +33,7 @@ namespace StatusMonitor.Web.Controllers.View
private readonly IMetricService _metricService;
private readonly IServiceProvider _provider;
private readonly ICleanService _cleanService;
private readonly IDataContext _context;
public AdminController(
......@@ -40,7 +41,8 @@ namespace StatusMonitor.Web.Controllers.View
ILogger<AdminController> logger,
IMetricService metricService,
IServiceProvider provider,
ICleanService cleanService
ICleanService cleanService,
IDataContext context
)
{
_metricService = metricService;
......@@ -48,6 +50,7 @@ namespace StatusMonitor.Web.Controllers.View
_loggingService = loggingService;
_provider = provider;
_cleanService = cleanService;
_context = context;
}
public async Task<IActionResult> Index()
......@@ -55,6 +58,11 @@ namespace StatusMonitor.Web.Controllers.View
ViewBag.Metrics = await _metricService
.GetMetricsAsync();
ViewBag.Discrepancies = await _context
.Discrepancies
.OrderByDescending(d => d.DateFirstOffense)
.ToListAsync();
return View();
}
......
......@@ -11,91 +11,177 @@
</div>
<div class="row">
<div class="col-md-3">
<div class="card">
<div class="card-header">
<h2>
Manual cleaunup
<small>
Run clean service manually with a specified max age
</small>
</h2>
</div>
<div class="card-body card-padding">
<div class="row">
<form
role="form"
asp-controller="Admin"
asp-action="Clean"
method="post"
novalidate
>
<div class="col-md-8 col-sm-12">
<h5>Set max age</h5>
<select class="selectpicker" name="maxAge">
<option value="0">Everything</option>
<option value="1">1 minutes</option>
<option value="10">10 minutes</option>
<option value="20">30 minutes</option>
<option value="60">1 hour</option>
<option value="240">4 hours</option>
<option value="720">12 hours</option>
<option value="1440">1 day</option>
<option value="4320">3 days</option>
<option value="10080">1 week</option>
<option value="43200">1 month</option>
</select>
<div class="col-md-6">
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h2>
Manual cleaunup
<small>
Run clean service manually with a specified max age
</small>
</h2>
</div>
<div class="card-body card-padding">
<div class="row">
<form
role="form"
asp-controller="Admin"
asp-action="Clean"
method="post"
novalidate
>
<div class="col-md-8 col-sm-12">
<h5>Set max age</h5>
<select class="selectpicker" name="maxAge">
<option value="0">Everything</option>
<option value="1">1 minutes</option>
<option value="10">10 minutes</option>
<option value="20">30 minutes</option>
<option value="60">1 hour</option>
<option value="240">4 hours</option>
<option value="720">12 hours</option>
<option value="1440">1 day</option>
<option value="4320">3 days</option>
<option value="10080">1 week</option>
<option value="43200">1 month</option>
</select>
</div>
<div class="col-md-4 col-sm-12">
<button type="submit" class="btn btn-danger btn-md waves-effect">Clean</button>
</div>
</form>
</div>
<div class="col-md-4 col-sm-12">
<button type="submit" class="btn btn-danger btn-md waves-effect">Clean</button>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h2>
Go to metric
<small>
Go to the metric's page
</small>
</h2>
</div>
<div class="card-body card-padding">
<div class="row">
<form
role="form"
asp-controller="Admin"
asp-action="Metric"
method="get"
novalidate
>
<div class="col-md-8 col-sm-12">
<h5>Metric</h5>
<select class="selectpicker" name="metric" data-live-search="true">
@foreach (var metric in ViewBag.Metrics)
{
<option value="@((Metrics)metric.Type)@@@(metric.Source)">@(metric.Title) from @(metric.Source)</option>
}
</select>
</div>
<div class="col-md-4 col-sm-12">
<button type="submit" class="btn btn-primary btn-md waves-effect">Go</button>
</div>
</form>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-header">
<h2>
Go to metric
<small>
Go to the metric's page
</small>
</h2>
</div>
<div class="card-body card-padding">
<div class="row">
<form
role="form"
asp-controller="Admin"
asp-action="Metric"
method="get"
novalidate
>
<div class="col-md-8 col-sm-12">
<h5>Metric</h5>
<select class="selectpicker" name="metric" data-live-search="true">
@foreach (var metric in ViewBag.Metrics)
{
<option value="@((Metrics)metric.Type)@@@(metric.Source)">@(metric.Title) from @(metric.Source)</option>
}
</select>
</div>
<div class="col-md-4 col-sm-12">
<button type="submit" class="btn btn-primary btn-md waves-effect">Go</button>
</div>
</form>
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h2>Discrepancies
<small>
View the list of resolved and unresolved dicrepancies from TODO until now
</small>
</h2>
</div>
<div class="card-body card-padding">
<div role="tabpanel">
<ul class="tab-nav" role="tablist">
<li class="active">
<a
href="#unresolved"
aria-controls="unresolved"
role="tab"
data-toggle="tab"
aria-expanded="true"
>
Unresolved ( @(((IEnumerable<Discrepancy>)ViewBag.Discrepancies).Count(d => !d.Resolved)) )
</a>
</li>
<li class="">
<a
href="#resovled"
aria-controls="resovled"
role="tab"
data-toggle="tab"
aria-expanded="false"
>
Resolved ( @(((IEnumerable<Discrepancy>)ViewBag.Discrepancies).Count(d => d.Resolved)) )
</a>
</li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="unresolved">
@if ( ((IEnumerable<Discrepancy>)ViewBag.Discrepancies).Any(d => !d.Resolved) )
{
@foreach (var dicrepancy in ((IEnumerable<Discrepancy>)ViewBag.Discrepancies).Where(d => !d.Resolved))
{
<vc:discrepancy-card model=dicrepancy></vc:discrepancy-card>
}
}
else
{
@: <h3>No outstanding issues! Well done!</h3>
}
</div>
<div role="tabpanel" class="tab-pane" id="resovled">
@if ( ((IEnumerable<Discrepancy>)ViewBag.Discrepancies).Any(d => d.Resolved) )
{
@foreach (var dicrepancy in ((IEnumerable<Discrepancy>)ViewBag.Discrepancies).Where(d => d.Resolved))
{
<vc:discrepancy-card model=dicrepancy></vc:discrepancy-card>
}
}
else
{
@: <h3>No discrepancies have been noticed.</h3>
}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
......
<div class="list-group-item media">
<div class="pull-left">
<i class="zmdi zmdi-@(Model.Resolved ? "check-circle" : "alert-circle-o") zmdi-hc-3x"></i>
</div>
@if (!Model.Resolved)
{
<div class="pull-right">
<div class="actions">
<!-- TODO form -->
<button type="submit" class="btn btn-success btn-md waves-effect">
Resolve
</button>
</div>
</div>
}
<div class="media-body">
<div class="lgi-heading">
Discrepancy of type <strong>@Model.Type</strong> from <em>@Model.MetricType</em> of <em>@Model.MetricSource</em>.
</div>
<small class="lgi-text">@Model.Description()</small>
<ul class="lgi-attrs">
<li>Date first offense: <utc-time time="@Model.DateFirstOffense" /></li>
@if (Model.Resolved)
{
<li>Date resolved: <utc-time time="@Model.DateResolved" /></li>
<li>Duration: @( Math.Round((@Model.DateResolved - @Model.DateFirstOffense).TotalSeconds) ) seconds</li>
}
</ul>
</div>
</div>
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using StatusMonitor.Shared.Extensions;
using StatusMonitor.Shared.Models.Entities;
namespace StatusMonitor.Web.Views.ViewComponents
{
/// <summary>
/// View component responsible for rendering discrepancy card.
/// </summary>
[ViewComponent(Name = "DiscrepancyCard")]
public class DiscrepancyCardViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(Discrepancy model)
{
await Task.CompletedTask;
return View(model);
}
}
}
......@@ -33,6 +33,20 @@ namespace StatusMonitor.Tests.ControllerTests
public AdminControllerTest()
{
var services = new ServiceCollection();
var mockEnv = new Mock<IHostingEnvironment>();
mockEnv
.SetupGet(environment => environment.EnvironmentName)
.Returns("Testing");
var env = mockEnv.Object;
services.RegisterSharedServices(env, new Mock<IConfiguration>().Object);
var context = services
.BuildServiceProvider()
.GetRequiredService<IDataContext>();
var mockServiceProvider = new Mock<IServiceProvider>();
mockServiceProvider
.Setup(provider => provider.GetService(typeof(IApiController)))
......@@ -43,7 +57,8 @@ namespace StatusMonitor.Tests.ControllerTests
new Mock<ILogger<AdminController>>().Object,
_mockMetricService.Object,
mockServiceProvider.Object,
_mockCleanService.Object
_mockCleanService.Object,
context
);
_controller.ControllerContext.HttpContext = new DefaultHttpContext();
_controller.TempData = new Mock<ITempDataDictionary>().Object;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment