Skip to content
Snippets Groups Projects
Commit f2406671 authored by Dmytro Bogatov's avatar Dmytro Bogatov :two_hearts:
Browse files

All done except tests.

parent ece9bc28
Branches
No related tags found
No related merge requests found
Pipeline #
# Badges
...@@ -26,7 +26,10 @@ ...@@ -26,7 +26,10 @@
- Different severities - with different frequencies - Different severities - with different frequencies
* Discrepancies * Discrepancies
- Detect discrepancies in data points (gaps, high values, ping failures) - Detect discrepancies in data points (gaps, high values, ping failures)
- Detect the start and end of discrepancy - not reported twice - Detect the start and the end of discrepancy - not reported twice
* Badges
- System health
- Individual metrics
* Rich API * Rich API
* Served as a docker composition - easy to install, configure and update * Served as a docker composition - easy to install, configure and update
* Different databases for old and recent data * Different databases for old and recent data
...@@ -49,6 +52,11 @@ ...@@ -49,6 +52,11 @@
!!! tip !!! tip
Detailed instruction can be found [here](configuration/). Detailed instruction can be found [here](configuration/).
## How to use badges
!!! tip
Detailed instruction can be found [here](badges/).
## A little story ## A little story
This project has started as a side project for the [RedwoodEDA](http://www.redwoodeda.com) - a helper tool to monitor [makerchip](http://makerchip.com) servers. This project has started as a side project for the [RedwoodEDA](http://www.redwoodeda.com) - a helper tool to monitor [makerchip](http://makerchip.com) servers.
... ...
......
...@@ -80,6 +80,13 @@ namespace StatusMonitor.Shared.Services ...@@ -80,6 +80,13 @@ namespace StatusMonitor.Shared.Services
); );
} }
if (await _context.HealthReports.AnyAsync(dp => dp.Timestamp < toTimestamp))
{
_context.HealthReports.RemoveRange(
_context.HealthReports.Where(dp => dp.Timestamp < toTimestamp)
);
}
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
_logger.LogDebug(LoggingEvents.Clean.AsInt(), "Cleaned old data."); _logger.LogDebug(LoggingEvents.Clean.AsInt(), "Cleaned old data.");
... ...
......
using StatusMonitor.Web.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using StatusMonitor.Shared.Extensions;
using System.Threading.Tasks;
using StatusMonitor.Shared.Models;
using StatusMonitor.Web.Services;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using StatusMonitor.Shared.Models.Entities;
using StatusMonitor.Shared.Services;
namespace StatusMonitor.Web.Controllers.View
{
/// <summary>
/// Controller responsible for health endpoints - /health
/// </summary>
public class HealthController : Controller
{
private readonly IMetricService _metricService;
private readonly IDataContext _context;
private readonly IAuthService _auth;
private readonly IBadgeService _badge;
public HealthController(
IMetricService metricService,
IDataContext context,
IAuthService auth,
IBadgeService badge
)
{
_metricService = metricService;
_context = context;
_auth = auth;
_badge = badge;
}
public async Task<IActionResult> Index()
{
return
await _context.HealthReports.CountAsync() > 0 ?
new BadgeResult(
_badge.GetSystemHealthBadge(
await _context.HealthReports.OrderByDescending(hp => hp.Timestamp).FirstAsync()
)
) :
(IActionResult)NoContent();
}
[Route("health/{type}/{source}")]
public async Task<IActionResult> Metric(string type, string source)
{
Metrics metricType;
try
{
metricType = type.ToEnum<Metrics>();
}
catch (System.Exception)
{
return BadRequest("Bad type. Needs to be one of Metrics type.");
}
var metrics = await _metricService.GetMetricsAsync(metricType, source);
if (metrics.Count() == 0)
{
return NotFound();
}
var metric = metrics.First();
if (!_auth.IsAuthenticated() && !metric.Public)
{
return Unauthorized();
}
return new BadgeResult(
_badge.GetMetricHealthBadge(
metric.Source, (Metrics)metric.Type, (AutoLabels)metric.AutoLabel.Id
)
);
}
}
}
...@@ -43,18 +43,6 @@ namespace StatusMonitor.Web.Controllers.View ...@@ -43,18 +43,6 @@ namespace StatusMonitor.Web.Controllers.View
_badge = badge; _badge = badge;
} }
public async Task<IActionResult> Health()
{
return
await _context.HealthReports.CountAsync() > 0 ?
new BadgeResult(
_badge.GetHealthBadge(
await _context.HealthReports.OrderByDescending(hp => hp.Timestamp).FirstAsync()
)
) :
(IActionResult)NoContent();
}
public async Task<IActionResult> Index() public async Task<IActionResult> Index()
{ {
... ...
......
...@@ -23,12 +23,30 @@ namespace StatusMonitor.Web.Services ...@@ -23,12 +23,30 @@ namespace StatusMonitor.Web.Services
/// </summary> /// </summary>
/// <param name="report">Health report from which to generate badge</param> /// <param name="report">Health report from which to generate badge</param>
/// <returns>Badge indicating overall health of the system</returns> /// <returns>Badge indicating overall health of the system</returns>
Badge GetHealthBadge(HealthReport report); Badge GetSystemHealthBadge(HealthReport report);
Badge GetMetricHealthBadge(string source, Metrics type, AutoLabels label);
} }
public class BadgeService : IBadgeService public class BadgeService : IBadgeService
{ {
public Badge GetHealthBadge(HealthReport report) public Badge GetMetricHealthBadge(string source, Metrics type, AutoLabels label)
{
return new Badge
{
Title = $"{type.ToString().ToLower()} of {source.ToLower()}",
Message = label.ToString(),
Status =
label == AutoLabels.Normal ?
BadgeStatus.Success :
(label == AutoLabels.Warning ?
BadgeStatus.Neutural :
BadgeStatus.Failure
)
};
}
public Badge GetSystemHealthBadge(HealthReport report)
{ {
return new Badge return new Badge
{ {
...@@ -40,9 +58,7 @@ namespace StatusMonitor.Web.Services ...@@ -40,9 +58,7 @@ namespace StatusMonitor.Web.Services
(report.Health >= 70 ? (report.Health >= 70 ?
BadgeStatus.Neutural : BadgeStatus.Neutural :
BadgeStatus.Failure BadgeStatus.Failure
), )
TitleWidth = 100,
MessageWidth = 40
}; };
} }
} }
...@@ -142,11 +158,25 @@ namespace StatusMonitor.Web.Services ...@@ -142,11 +158,25 @@ namespace StatusMonitor.Web.Services
/// <summary> /// <summary>
/// Width in px of the title /// Width in px of the title
/// </summary> /// </summary>
public int TitleWidth { get; set; } public int TitleWidth
{
get
{
return Title.Length * 8;
}
private set { }
}
/// <summary> /// <summary>
/// Width in px of the message /// Width in px of the message
/// </summary> /// </summary>
public int MessageWidth { get; set; } public int MessageWidth
{
get
{
return (int)Math.Round(Message.Length * 12.5);
}
private set { }
}
/// <summary> /// <summary>
/// HEX color representation of the badge semantic meaning /// HEX color representation of the badge semantic meaning
... ...
......
using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using StatusMonitor.Web.Controllers.View;
using StatusMonitor.Shared.Extensions;
using StatusMonitor.Shared.Models.Entities;
using StatusMonitor.Shared.Services;
using Xunit;
using Microsoft.AspNetCore.Http;
using Moq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using StatusMonitor.Shared.Models;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using StatusMonitor.Web.Services;
namespace StatusMonitor.Tests.ControllerTests
{
public partial class HealthControllerTest
{
private readonly Mock<IMetricService> _mockMetricService = new Mock<IMetricService>();
private readonly Mock<IAuthService> _mockAuth = new Mock<IAuthService>();
private readonly Mock<IBadgeService> _mockBadge = new Mock<IBadgeService>();
private readonly IDataContext _context;
private readonly HealthController _controller;
public HealthControllerTest()
{
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);
_context = services
.BuildServiceProvider()
.GetRequiredService<IDataContext>();
_context.ManualLabels.Add(new ManualLabel { Id = ManualLabels.None.AsInt() });
_context.SaveChanges();
_mockAuth
.Setup(auth => auth.IsAuthenticated())
.Returns(true);
_controller = new HealthController(
_mockMetricService.Object,
_context,
_mockAuth.Object,
_mockBadge.Object
);
_controller.ControllerContext.HttpContext = new DefaultHttpContext();
_controller.TempData = new Mock<ITempDataDictionary>().Object;
}
}
}
...@@ -21,10 +21,10 @@ using System.Linq; ...@@ -21,10 +21,10 @@ using System.Linq;
namespace StatusMonitor.Tests.ControllerTests namespace StatusMonitor.Tests.ControllerTests
{ {
public partial class HomeControllerTest public partial class HealthControllerTest
{ {
[Fact] [Fact]
public async Task Health() public async Task Index()
{ {
// Arrange // Arrange
await _context.HealthReports.AddAsync(new HealthReport()); await _context.HealthReports.AddAsync(new HealthReport());
...@@ -33,17 +33,17 @@ namespace StatusMonitor.Tests.ControllerTests ...@@ -33,17 +33,17 @@ namespace StatusMonitor.Tests.ControllerTests
var badge = new Badge { var badge = new Badge {
Title = "System health", Title = "System health",
Message = "95%", Message = "95%",
Status = BadgeStatus.Success, Status = BadgeStatus.Success
TitleWidth = 100, // TitleWidth = 100,
MessageWidth = 40 // MessageWidth = 40
}; };
_mockBadge _mockBadge
.Setup(mock => mock.GetHealthBadge(It.IsAny<HealthReport>())) .Setup(mock => mock.GetSystemHealthBadge(It.IsAny<HealthReport>()))
.Returns(badge); .Returns(badge);
// Act // Act
var result = await _controller.Health(); var result = await _controller.Index();
// Assert // Assert
var badgeResult = Assert.IsType<BadgeResult>(result); var badgeResult = Assert.IsType<BadgeResult>(result);
...@@ -56,10 +56,10 @@ namespace StatusMonitor.Tests.ControllerTests ...@@ -56,10 +56,10 @@ namespace StatusMonitor.Tests.ControllerTests
} }
[Fact] [Fact]
public async Task HealthNoData() public async Task IndexNoData()
{ {
// Act // Act
var result = await _controller.Health(); var result = await _controller.Index();
// Assert // Assert
var noContentResult = Assert.IsType<NoContentResult>(result); var noContentResult = Assert.IsType<NoContentResult>(result);
... ...
......
...@@ -15,7 +15,7 @@ namespace StatusMonitor.Tests.UnitTests.Services ...@@ -15,7 +15,7 @@ namespace StatusMonitor.Tests.UnitTests.Services
var badgeService = new BadgeService(); var badgeService = new BadgeService();
// Act // Act
var badge = badgeService.GetHealthBadge(new HealthReport()); var badge = badgeService.GetSystemHealthBadge(new HealthReport());
// Assert // Assert
Assert.Equal(BadgeStatus.Failure, badge.Status); Assert.Equal(BadgeStatus.Failure, badge.Status);
...@@ -79,7 +79,7 @@ namespace StatusMonitor.Tests.UnitTests.Services ...@@ -79,7 +79,7 @@ namespace StatusMonitor.Tests.UnitTests.Services
var report = new HealthReport { Data = dataPoints }; var report = new HealthReport { Data = dataPoints };
// Act // Act
var badge = badgeService.GetHealthBadge(report); var badge = badgeService.GetSystemHealthBadge(report);
// Assert // Assert
Assert.Equal(status, badge.Status); Assert.Equal(status, badge.Status);
... ...
......
...@@ -117,6 +117,12 @@ namespace StatusMonitor.Tests.UnitTests.Services ...@@ -117,6 +117,12 @@ namespace StatusMonitor.Tests.UnitTests.Services
} }
}); });
await context.HealthReports.AddRangeAsync(
new HealthReport { Timestamp = twoDaysAgo },
new HealthReport { Timestamp = hourAgo },
new HealthReport { Timestamp = now }
);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
// Act // Act
...@@ -133,8 +139,10 @@ namespace StatusMonitor.Tests.UnitTests.Services ...@@ -133,8 +139,10 @@ namespace StatusMonitor.Tests.UnitTests.Services
Assert.Equal(2, await context.UserActionDataPoints.CountAsync()); Assert.Equal(2, await context.UserActionDataPoints.CountAsync());
Assert.Equal(2, await context.CompilationDataPoints.CountAsync()); Assert.Equal(2, await context.CompilationDataPoints.CountAsync());
Assert.Equal(2, await context.PingDataPoints.CountAsync()); Assert.Equal(2, await context.PingDataPoints.CountAsync());
Assert.Equal(2, await context.LogEntries.CountAsync());
Assert.Equal(3, await context.Notifications.CountAsync()); Assert.Equal(3, await context.Notifications.CountAsync());
Assert.Equal(2, await context.Discrepancies.CountAsync()); Assert.Equal(2, await context.Discrepancies.CountAsync());
Assert.Equal(2, await context.HealthReports.CountAsync());
} }
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment