New: Added health check warning to emphasis when a series was deleted instead of only logging it in System Events
This commit is contained in:
parent
ceaaec5378
commit
8a2a41fab0
|
@ -0,0 +1,77 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FizzWare.NBuilder;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.HealthCheck.Checks;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.HealthCheck.Checks
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class RemovedSeriesCheckFixture : CoreTest<RemovedSeriesCheck>
|
||||||
|
{
|
||||||
|
private void GivenSeries(int amount, int deleted)
|
||||||
|
{
|
||||||
|
List<Series> series;
|
||||||
|
|
||||||
|
if (amount == 0)
|
||||||
|
{
|
||||||
|
series = new List<Series>();
|
||||||
|
}
|
||||||
|
else if (deleted == 0)
|
||||||
|
{
|
||||||
|
series = Builder<Series>.CreateListOfSize(amount)
|
||||||
|
.All()
|
||||||
|
.With(v => v.Status = SeriesStatusType.Continuing)
|
||||||
|
.BuildList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
series = Builder<Series>.CreateListOfSize(amount)
|
||||||
|
.All()
|
||||||
|
.With(v => v.Status = SeriesStatusType.Continuing)
|
||||||
|
.Random(deleted)
|
||||||
|
.With(v => v.Status = SeriesStatusType.Deleted)
|
||||||
|
.BuildList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mocker.GetMock<ISeriesService>()
|
||||||
|
.Setup(v => v.GetAllSeries())
|
||||||
|
.Returns(series);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_error_if_series_no_longer_on_tvdb()
|
||||||
|
{
|
||||||
|
GivenSeries(4, 1);
|
||||||
|
|
||||||
|
Subject.Check().ShouldBeError();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_error_if_multiple_series_no_longer_on_tvdb()
|
||||||
|
{
|
||||||
|
GivenSeries(4, 2);
|
||||||
|
|
||||||
|
Subject.Check().ShouldBeError();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_ok_if_all_series_still_on_tvdb()
|
||||||
|
{
|
||||||
|
GivenSeries(4, 0);
|
||||||
|
|
||||||
|
Subject.Check().ShouldBeOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_ok_if_no_series_exist()
|
||||||
|
{
|
||||||
|
GivenSeries(0, 0);
|
||||||
|
|
||||||
|
Subject.Check().ShouldBeOk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -123,6 +123,30 @@ namespace NzbDrone.Core.Test.TvTests
|
||||||
ExceptionVerification.ExpectedErrors(1);
|
ExceptionVerification.ExpectedErrors(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_mark_as_deleted_if_tvdb_id_not_found()
|
||||||
|
{
|
||||||
|
Subject.Execute(new RefreshSeriesCommand(_series.Id));
|
||||||
|
|
||||||
|
Mocker.GetMock<ISeriesService>()
|
||||||
|
.Verify(v => v.UpdateSeries(It.Is<Series>(s => s.Status == SeriesStatusType.Deleted), It.IsAny<bool>()), Times.Once());
|
||||||
|
|
||||||
|
ExceptionVerification.ExpectedErrors(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_not_remark_as_deleted_if_tvdb_id_not_found()
|
||||||
|
{
|
||||||
|
_series.Status = SeriesStatusType.Deleted;
|
||||||
|
|
||||||
|
Subject.Execute(new RefreshSeriesCommand(_series.Id));
|
||||||
|
|
||||||
|
Mocker.GetMock<ISeriesService>()
|
||||||
|
.Verify(v => v.UpdateSeries(It.IsAny<Series>(), It.IsAny<bool>()), Times.Never());
|
||||||
|
|
||||||
|
ExceptionVerification.ExpectedErrors(1);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_update_if_tvdb_id_changed()
|
public void should_update_if_tvdb_id_changed()
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,7 @@ using System;
|
||||||
namespace NzbDrone.Core.HealthCheck
|
namespace NzbDrone.Core.HealthCheck
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
|
||||||
public class CheckOnAttribute: Attribute
|
public class CheckOnAttribute : Attribute
|
||||||
{
|
{
|
||||||
public Type EventType { get; set; }
|
public Type EventType { get; set; }
|
||||||
public CheckOnCondition Condition { get; set; }
|
public CheckOnCondition Condition { get; set; }
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
using NzbDrone.Core.Tv.Events;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.HealthCheck.Checks
|
||||||
|
{
|
||||||
|
[CheckOn(typeof(SeriesUpdatedEvent))]
|
||||||
|
[CheckOn(typeof(SeriesDeletedEvent), CheckOnCondition.FailedOnly)]
|
||||||
|
public class RemovedSeriesCheck : HealthCheckBase, ICheckOnCondition<SeriesUpdatedEvent>, ICheckOnCondition<SeriesDeletedEvent>
|
||||||
|
{
|
||||||
|
private readonly ISeriesService _seriesService;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public RemovedSeriesCheck(ISeriesService seriesService, Logger logger)
|
||||||
|
{
|
||||||
|
_seriesService = seriesService;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override HealthCheck Check()
|
||||||
|
{
|
||||||
|
var deletedSeries = _seriesService.GetAllSeries().Where(v => v.Status == SeriesStatusType.Deleted).ToList();
|
||||||
|
|
||||||
|
if (deletedSeries.Empty())
|
||||||
|
{
|
||||||
|
return new HealthCheck(GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
var seriesText = deletedSeries.Select(s => $"{s.Title} (tvdbid {s.TvdbId})").Join(", ");
|
||||||
|
|
||||||
|
if (deletedSeries.Count() == 1)
|
||||||
|
{
|
||||||
|
|
||||||
|
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Series {seriesText} was removed from TheTVDB");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HealthCheck(GetType(), HealthCheckResult.Error, $"Series {seriesText} were removed from TheTVDB");
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShouldCheckOnEvent(SeriesDeletedEvent deletedEvent)
|
||||||
|
{
|
||||||
|
return deletedEvent.Series.Status == SeriesStatusType.Deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShouldCheckOnEvent(SeriesUpdatedEvent updatedEvent)
|
||||||
|
{
|
||||||
|
return updatedEvent.Series.Status == SeriesStatusType.Deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,46 @@
|
||||||
|
using System;
|
||||||
|
using NzbDrone.Common.Messaging;
|
||||||
|
|
||||||
namespace NzbDrone.Core.HealthCheck
|
namespace NzbDrone.Core.HealthCheck
|
||||||
{
|
{
|
||||||
public class EventDrivenHealthCheck
|
public interface IEventDrivenHealthCheck
|
||||||
|
{
|
||||||
|
IProvideHealthCheck HealthCheck { get; }
|
||||||
|
|
||||||
|
bool ShouldExecute(IEvent message, bool previouslyFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EventDrivenHealthCheck<TEvent> : IEventDrivenHealthCheck
|
||||||
{
|
{
|
||||||
public IProvideHealthCheck HealthCheck { get; set; }
|
public IProvideHealthCheck HealthCheck { get; set; }
|
||||||
public CheckOnCondition Condition { get; set; }
|
public CheckOnCondition Condition { get; set; }
|
||||||
|
public ICheckOnCondition<TEvent> EventFilter { get; set; }
|
||||||
|
|
||||||
public EventDrivenHealthCheck(IProvideHealthCheck healthCheck, CheckOnCondition condition)
|
public EventDrivenHealthCheck(IProvideHealthCheck healthCheck, CheckOnCondition condition)
|
||||||
{
|
{
|
||||||
HealthCheck = healthCheck;
|
HealthCheck = healthCheck;
|
||||||
Condition = condition;
|
Condition = condition;
|
||||||
|
EventFilter = healthCheck as ICheckOnCondition<TEvent>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShouldExecute(IEvent message, bool previouslyFailed)
|
||||||
|
{
|
||||||
|
if (Condition == CheckOnCondition.SuccessfulOnly && previouslyFailed)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Condition == CheckOnCondition.FailedOnly && !previouslyFailed)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EventFilter == null && !EventFilter.ShouldCheckOnEvent((TEvent)message))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace NzbDrone.Core.HealthCheck
|
||||||
private readonly IProvideHealthCheck[] _healthChecks;
|
private readonly IProvideHealthCheck[] _healthChecks;
|
||||||
private readonly IProvideHealthCheck[] _startupHealthChecks;
|
private readonly IProvideHealthCheck[] _startupHealthChecks;
|
||||||
private readonly IProvideHealthCheck[] _scheduledHealthChecks;
|
private readonly IProvideHealthCheck[] _scheduledHealthChecks;
|
||||||
private readonly Dictionary<Type, EventDrivenHealthCheck[]> _eventDrivenHealthChecks;
|
private readonly Dictionary<Type, IEventDrivenHealthCheck[]> _eventDrivenHealthChecks;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly ICacheManager _cacheManager;
|
private readonly ICacheManager _cacheManager;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
@ -58,10 +58,16 @@ namespace NzbDrone.Core.HealthCheck
|
||||||
return _healthCheckResults.Values.ToList();
|
return _healthCheckResults.Values.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<Type, EventDrivenHealthCheck[]> GetEventDrivenHealthChecks()
|
private Dictionary<Type, IEventDrivenHealthCheck[]> GetEventDrivenHealthChecks()
|
||||||
{
|
{
|
||||||
return _healthChecks
|
return _healthChecks
|
||||||
.SelectMany(h => h.GetType().GetAttributes<CheckOnAttribute>().Select(a => Tuple.Create(a.EventType, new EventDrivenHealthCheck(h, a.Condition))))
|
.SelectMany(h => h.GetType().GetAttributes<CheckOnAttribute>().Select(a =>
|
||||||
|
{
|
||||||
|
var eventDrivenType = typeof(EventDrivenHealthCheck<>).MakeGenericType(a.EventType);
|
||||||
|
var eventDriven = (IEventDrivenHealthCheck)Activator.CreateInstance(eventDrivenType, h, a.Condition);
|
||||||
|
|
||||||
|
return Tuple.Create(a.EventType, eventDriven);
|
||||||
|
}))
|
||||||
.GroupBy(t => t.Item1, t => t.Item2)
|
.GroupBy(t => t.Item1, t => t.Item2)
|
||||||
.ToDictionary(g => g.Key, g => g.ToArray());
|
.ToDictionary(g => g.Key, g => g.ToArray());
|
||||||
}
|
}
|
||||||
|
@ -111,7 +117,7 @@ namespace NzbDrone.Core.HealthCheck
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EventDrivenHealthCheck[] checks;
|
IEventDrivenHealthCheck[] checks;
|
||||||
if (!_eventDrivenHealthChecks.TryGetValue(message.GetType(), out checks))
|
if (!_eventDrivenHealthChecks.TryGetValue(message.GetType(), out checks))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -122,26 +128,14 @@ namespace NzbDrone.Core.HealthCheck
|
||||||
|
|
||||||
foreach (var eventDrivenHealthCheck in checks)
|
foreach (var eventDrivenHealthCheck in checks)
|
||||||
{
|
{
|
||||||
if (eventDrivenHealthCheck.Condition == CheckOnCondition.Always)
|
|
||||||
{
|
|
||||||
filteredChecks.Add(eventDrivenHealthCheck.HealthCheck);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var healthCheckType = eventDrivenHealthCheck.HealthCheck.GetType();
|
var healthCheckType = eventDrivenHealthCheck.HealthCheck.GetType();
|
||||||
|
var previouslyFailed = healthCheckResults.Any(r => r.Source == healthCheckType);
|
||||||
|
|
||||||
if (eventDrivenHealthCheck.Condition == CheckOnCondition.FailedOnly &&
|
if (eventDrivenHealthCheck.ShouldExecute(message, previouslyFailed))
|
||||||
healthCheckResults.Any(r => r.Source == healthCheckType))
|
|
||||||
{
|
{
|
||||||
filteredChecks.Add(eventDrivenHealthCheck.HealthCheck);
|
filteredChecks.Add(eventDrivenHealthCheck.HealthCheck);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventDrivenHealthCheck.Condition == CheckOnCondition.SuccessfulOnly &&
|
|
||||||
healthCheckResults.None(r => r.Source == healthCheckType))
|
|
||||||
{
|
|
||||||
filteredChecks.Add(eventDrivenHealthCheck.HealthCheck);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace NzbDrone.Core.HealthCheck
|
||||||
|
{
|
||||||
|
public interface ICheckOnCondition<TEvent>
|
||||||
|
{
|
||||||
|
bool ShouldCheckOnEvent(TEvent message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,9 +54,26 @@ namespace NzbDrone.Core.Tv
|
||||||
{
|
{
|
||||||
_logger.ProgressInfo("Updating {0}", series.Title);
|
_logger.ProgressInfo("Updating {0}", series.Title);
|
||||||
|
|
||||||
var tuple = _seriesInfo.GetSeriesInfo(series.TvdbId);
|
Series seriesInfo;
|
||||||
|
List<Episode> episodes;
|
||||||
|
|
||||||
var seriesInfo = tuple.Item1;
|
try
|
||||||
|
{
|
||||||
|
var tuple = _seriesInfo.GetSeriesInfo(series.TvdbId);
|
||||||
|
seriesInfo = tuple.Item1;
|
||||||
|
episodes = tuple.Item2;
|
||||||
|
}
|
||||||
|
catch (SeriesNotFoundException)
|
||||||
|
{
|
||||||
|
if (series.Status != SeriesStatusType.Deleted)
|
||||||
|
{
|
||||||
|
series.Status = SeriesStatusType.Deleted;
|
||||||
|
_seriesService.UpdateSeries(series);
|
||||||
|
_logger.Debug("Series marked as deleted on tvdb for {0}", series.Title);
|
||||||
|
_eventAggregator.PublishEvent(new SeriesUpdatedEvent(series));
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
if (series.TvdbId != seriesInfo.TvdbId)
|
if (series.TvdbId != seriesInfo.TvdbId)
|
||||||
{
|
{
|
||||||
|
@ -102,7 +119,7 @@ namespace NzbDrone.Core.Tv
|
||||||
series.Seasons = UpdateSeasons(series, seriesInfo);
|
series.Seasons = UpdateSeasons(series, seriesInfo);
|
||||||
|
|
||||||
_seriesService.UpdateSeries(series);
|
_seriesService.UpdateSeries(series);
|
||||||
_refreshEpisodeService.RefreshEpisodeInfo(series, tuple.Item2);
|
_refreshEpisodeService.RefreshEpisodeInfo(series, episodes);
|
||||||
|
|
||||||
_logger.Debug("Finished series refresh for {0}", series.Title);
|
_logger.Debug("Finished series refresh for {0}", series.Title);
|
||||||
_eventAggregator.PublishEvent(new SeriesUpdatedEvent(series));
|
_eventAggregator.PublishEvent(new SeriesUpdatedEvent(series));
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
{
|
{
|
||||||
public enum SeriesStatusType
|
public enum SeriesStatusType
|
||||||
{
|
{
|
||||||
|
Deleted = -1,
|
||||||
Continuing = 0,
|
Continuing = 0,
|
||||||
Ended = 1
|
Ended = 1,
|
||||||
|
Upcoming = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue