Browse Source

Enable nullable reference types for Emby.Photos and Emby.Notifications

* Enable TreatWarningsAsErrors for Emby.Notifications
* Add analyzers to Emby.Notifications
Bond_009 5 years ago
parent
commit
5dc3874ebd

+ 11 - 104
Emby.Notifications/Api/NotificationsService.cs

@@ -1,3 +1,8 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1402
+#pragma warning disable SA1600
+#pragma warning disable SA1649
+
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -12,60 +17,6 @@ using MediaBrowser.Model.Services;
 
 namespace Emby.Notifications.Api
 {
-    [Route("/Notifications/{UserId}", "GET", Summary = "Gets notifications")]
-    public class GetNotifications : IReturn<NotificationResult>
-    {
-        [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public string UserId { get; set; }
-
-        [ApiMember(Name = "IsRead", Description = "An optional filter by IsRead", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
-        public bool? IsRead { get; set; }
-
-        [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
-        public int? StartIndex { get; set; }
-
-        [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
-        public int? Limit { get; set; }
-    }
-
-    public class Notification
-    {
-        public string Id { get; set; }
-
-        public string UserId { get; set; }
-
-        public DateTime Date { get; set; }
-
-        public bool IsRead { get; set; }
-
-        public string Name { get; set; }
-
-        public string Description { get; set; }
-
-        public string Url { get; set; }
-
-        public NotificationLevel Level { get; set; }
-    }
-
-    public class NotificationResult
-    {
-        public Notification[] Notifications { get; set; }
-        public int TotalRecordCount { get; set; }
-    }
-
-    public class NotificationsSummary
-    {
-        public int UnreadCount { get; set; }
-        public NotificationLevel MaxUnreadNotificationLevel { get; set; }
-    }
-
-    [Route("/Notifications/{UserId}/Summary", "GET", Summary = "Gets a notification summary for a user")]
-    public class GetNotificationsSummary : IReturn<NotificationsSummary>
-    {
-        [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public string UserId { get; set; }
-    }
-
     [Route("/Notifications/Types", "GET", Summary = "Gets notification types")]
     public class GetNotificationTypes : IReturn<List<NotificationTypeInfo>>
     {
@@ -80,41 +31,21 @@ namespace Emby.Notifications.Api
     public class AddAdminNotification : IReturnVoid
     {
         [ApiMember(Name = "Name", Description = "The notification's name", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
-        public string Name { get; set; }
+        public string Name { get; set; } = string.Empty;
 
         [ApiMember(Name = "Description", Description = "The notification's description", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")]
-        public string Description { get; set; }
+        public string Description { get; set; } = string.Empty;
 
         [ApiMember(Name = "ImageUrl", Description = "The notification's image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
-        public string ImageUrl { get; set; }
+        public string? ImageUrl { get; set; }
 
         [ApiMember(Name = "Url", Description = "The notification's info url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
-        public string Url { get; set; }
+        public string? Url { get; set; }
 
         [ApiMember(Name = "Level", Description = "The notification level", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
         public NotificationLevel Level { get; set; }
     }
 
-    [Route("/Notifications/{UserId}/Read", "POST", Summary = "Marks notifications as read")]
-    public class MarkRead : IReturnVoid
-    {
-        [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public string UserId { get; set; }
-
-        [ApiMember(Name = "Ids", Description = "A list of notification ids, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
-        public string Ids { get; set; }
-    }
-
-    [Route("/Notifications/{UserId}/Unread", "POST", Summary = "Marks notifications as unread")]
-    public class MarkUnread : IReturnVoid
-    {
-        [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public string UserId { get; set; }
-
-        [ApiMember(Name = "Ids", Description = "A list of notification ids, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
-        public string Ids { get; set; }
-    }
-
     [Authenticated]
     public class NotificationsService : IService
     {
@@ -129,30 +60,19 @@ namespace Emby.Notifications.Api
 
         public object Get(GetNotificationTypes request)
         {
+            _ = request; // Silence unused variable warning
             return _notificationManager.GetNotificationTypes();
         }
 
         public object Get(GetNotificationServices request)
         {
+            _ = request; // Silence unused variable warning
             return _notificationManager.GetNotificationServices().ToList();
         }
 
-        public object Get(GetNotificationsSummary request)
-        {
-            return new NotificationsSummary
-            {
-
-            };
-        }
-
         public Task Post(AddAdminNotification request)
         {
             // This endpoint really just exists as post of a real with sickbeard
-            return AddNotification(request);
-        }
-
-        private Task AddNotification(AddAdminNotification request)
-        {
             var notification = new NotificationRequest
             {
                 Date = DateTime.UtcNow,
@@ -165,18 +85,5 @@ namespace Emby.Notifications.Api
 
             return _notificationManager.SendNotification(notification, CancellationToken.None);
         }
-
-        public void Post(MarkRead request)
-        {
-        }
-
-        public void Post(MarkUnread request)
-        {
-        }
-
-        public object Get(GetNotifications request)
-        {
-            return new NotificationResult();
-        }
     }
 }

+ 3 - 1
Emby.Notifications/CoreNotificationTypes.cs

@@ -1,7 +1,9 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using MediaBrowser.Controller;
 using MediaBrowser.Controller.Notifications;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Notifications;

+ 14 - 0
Emby.Notifications/Emby.Notifications.csproj

@@ -4,6 +4,8 @@
     <TargetFramework>netstandard2.1</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <Nullable>enable</Nullable>
   </PropertyGroup>
 
   <ItemGroup>
@@ -16,4 +18,16 @@
     <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
   </ItemGroup>
 
+  <!-- Code analyzers-->
+  <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
+    <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8" PrivateAssets="All" />
+    <PackageReference Include="SerilogAnalyzer" Version="0.15.0" PrivateAssets="All" />
+    <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
+    <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
+  </ItemGroup>
+
+  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
+    <CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+
 </Project>

+ 4 - 1
Emby.Notifications/NotificationConfigurationFactory.cs

@@ -1,3 +1,6 @@
+#pragma warning disable CS1591
+#pragma warning disable SA1600
+
 using System.Collections.Generic;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Model.Notifications;
@@ -13,7 +16,7 @@ namespace Emby.Notifications
                 new ConfigurationStore
                 {
                     Key = "notifications",
-                    ConfigurationType = typeof (NotificationOptions)
+                    ConfigurationType = typeof(NotificationOptions)
                 }
             };
         }

+ 76 - 41
Emby.Notifications/Notifications.cs → Emby.Notifications/NotificationEntryPoint.cs

@@ -21,70 +21,85 @@ using Microsoft.Extensions.Logging;
 namespace Emby.Notifications
 {
     /// <summary>
-    /// Creates notifications for various system events
+    /// Creates notifications for various system events.
     /// </summary>
-    public class Notifications : IServerEntryPoint
+    public class NotificationEntryPoint : IServerEntryPoint
     {
         private readonly ILogger _logger;
-
+        private readonly IActivityManager _activityManager;
+        private readonly ILocalizationManager _localization;
         private readonly INotificationManager _notificationManager;
-
         private readonly ILibraryManager _libraryManager;
         private readonly IServerApplicationHost _appHost;
+        private readonly IConfigurationManager _config;
 
-        private Timer LibraryUpdateTimer { get; set; }
         private readonly object _libraryChangedSyncLock = new object();
+        private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
 
-        private readonly IConfigurationManager _config;
-        private readonly ILocalizationManager _localization;
-        private readonly IActivityManager _activityManager;
+        private Timer? _libraryUpdateTimer;
 
         private string[] _coreNotificationTypes;
 
-        public Notifications(
+        private bool _disposed = false;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NotificationEntryPoint" /> class.
+        /// </summary>
+        /// <param name="logger">The logger.</param>
+        /// <param name="activityManager">The activity manager.</param>
+        /// <param name="localization">The localization manager.</param>
+        /// <param name="notificationManager">The notification manager.</param>
+        /// <param name="libraryManager">The library manager.</param>
+        /// <param name="appHost">The application host.</param>
+        /// <param name="config">The configuration manager.</param>
+        public NotificationEntryPoint(
+            ILogger<NotificationEntryPoint> logger,
             IActivityManager activityManager,
             ILocalizationManager localization,
-            ILogger logger,
             INotificationManager notificationManager,
             ILibraryManager libraryManager,
             IServerApplicationHost appHost,
             IConfigurationManager config)
         {
             _logger = logger;
+            _activityManager = activityManager;
+            _localization = localization;
             _notificationManager = notificationManager;
             _libraryManager = libraryManager;
             _appHost = appHost;
             _config = config;
-            _localization = localization;
-            _activityManager = activityManager;
 
             _coreNotificationTypes = new CoreNotificationTypes(localization).GetNotificationTypes().Select(i => i.Type).ToArray();
         }
 
+        /// <inheritdoc />
         public Task RunAsync()
         {
-            _libraryManager.ItemAdded += _libraryManager_ItemAdded;
-            _appHost.HasPendingRestartChanged += _appHost_HasPendingRestartChanged;
-            _appHost.HasUpdateAvailableChanged += _appHost_HasUpdateAvailableChanged;
-            _activityManager.EntryCreated += _activityManager_EntryCreated;
+            _libraryManager.ItemAdded += OnLibraryManagerItemAdded;
+            _appHost.HasPendingRestartChanged += OnAppHostHasPendingRestartChanged;
+            _appHost.HasUpdateAvailableChanged += OnAppHostHasUpdateAvailableChanged;
+            _activityManager.EntryCreated += OnActivityManagerEntryCreated;
 
             return Task.CompletedTask;
         }
 
-        private async void _appHost_HasPendingRestartChanged(object sender, EventArgs e)
+        private async void OnAppHostHasPendingRestartChanged(object sender, EventArgs e)
         {
             var type = NotificationType.ServerRestartRequired.ToString();
 
             var notification = new NotificationRequest
             {
                 NotificationType = type,
-                Name = string.Format(_localization.GetLocalizedString("ServerNameNeedsToBeRestarted"), _appHost.Name)
+                Name = string.Format(
+                    CultureInfo.InvariantCulture,
+                    _localization.GetLocalizedString("ServerNameNeedsToBeRestarted"),
+                    _appHost.Name)
             };
 
             await SendNotification(notification, null).ConfigureAwait(false);
         }
 
-        private async void _activityManager_EntryCreated(object sender, GenericEventArgs<ActivityLogEntry> e)
+        private async void OnActivityManagerEntryCreated(object sender, GenericEventArgs<ActivityLogEntry> e)
         {
             var entry = e.Argument;
 
@@ -117,7 +132,7 @@ namespace Emby.Notifications
             return _config.GetConfiguration<NotificationOptions>("notifications");
         }
 
-        private async void _appHost_HasUpdateAvailableChanged(object sender, EventArgs e)
+        private async void OnAppHostHasUpdateAvailableChanged(object sender, EventArgs e)
         {
             if (!_appHost.HasUpdateAvailable)
             {
@@ -136,8 +151,7 @@ namespace Emby.Notifications
             await SendNotification(notification, null).ConfigureAwait(false);
         }
 
-        private readonly List<BaseItem> _itemsAdded = new List<BaseItem>();
-        private void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
+        private void OnLibraryManagerItemAdded(object sender, ItemChangeEventArgs e)
         {
             if (!FilterItem(e.Item))
             {
@@ -146,14 +160,17 @@ namespace Emby.Notifications
 
             lock (_libraryChangedSyncLock)
             {
-                if (LibraryUpdateTimer == null)
+                if (_libraryUpdateTimer == null)
                 {
-                    LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, 5000,
-                                                   Timeout.Infinite);
+                    _libraryUpdateTimer = new Timer(
+                        LibraryUpdateTimerCallback,
+                        null,
+                        5000,
+                        Timeout.Infinite);
                 }
                 else
                 {
-                    LibraryUpdateTimer.Change(5000, Timeout.Infinite);
+                    _libraryUpdateTimer.Change(5000, Timeout.Infinite);
                 }
 
                 _itemsAdded.Add(e.Item);
@@ -188,7 +205,8 @@ namespace Emby.Notifications
             {
                 items = _itemsAdded.ToList();
                 _itemsAdded.Clear();
-                DisposeLibraryUpdateTimer();
+                _libraryUpdateTimer!.Dispose(); // Shouldn't be null as it just set off this callback
+                _libraryUpdateTimer = null;
             }
 
             items = items.Take(10).ToList();
@@ -198,7 +216,10 @@ namespace Emby.Notifications
                 var notification = new NotificationRequest
                 {
                     NotificationType = NotificationType.NewLibraryContent.ToString(),
-                    Name = string.Format(_localization.GetLocalizedString("ValueHasBeenAddedToLibrary"), GetItemName(item)),
+                    Name = string.Format(
+                        CultureInfo.InvariantCulture,
+                        _localization.GetLocalizedString("ValueHasBeenAddedToLibrary"),
+                        GetItemName(item)),
                     Description = item.Overview
                 };
 
@@ -206,7 +227,7 @@ namespace Emby.Notifications
             }
         }
 
-        public static string GetItemName(BaseItem item)
+        private static string GetItemName(BaseItem item)
         {
             var name = item.Name;
             if (item is Episode episode)
@@ -219,6 +240,7 @@ namespace Emby.Notifications
                         episode.IndexNumber.Value,
                         name);
                 }
+
                 if (episode.ParentIndexNumber.HasValue)
                 {
                     name = string.Format(
@@ -229,7 +251,6 @@ namespace Emby.Notifications
                 }
             }
 
-
             if (item is IHasSeries hasSeries)
             {
                 name = hasSeries.SeriesName + " - " + name;
@@ -257,7 +278,7 @@ namespace Emby.Notifications
             return name;
         }
 
-        private async Task SendNotification(NotificationRequest notification, BaseItem relatedItem)
+        private async Task SendNotification(NotificationRequest notification, BaseItem? relatedItem)
         {
             try
             {
@@ -269,23 +290,37 @@ namespace Emby.Notifications
             }
         }
 
+        /// <inheritdoc />
         public void Dispose()
         {
-            DisposeLibraryUpdateTimer();
-
-            _libraryManager.ItemAdded -= _libraryManager_ItemAdded;
-            _appHost.HasPendingRestartChanged -= _appHost_HasPendingRestartChanged;
-            _appHost.HasUpdateAvailableChanged -= _appHost_HasUpdateAvailableChanged;
-            _activityManager.EntryCreated -= _activityManager_EntryCreated;
+            Dispose(true);
+            GC.SuppressFinalize(this);
         }
 
-        private void DisposeLibraryUpdateTimer()
+        /// <summary>
+        /// Releases unmanaged and - optionally - managed resources.
+        /// </summary>
+        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+        protected virtual void Dispose(bool disposing)
         {
-            if (LibraryUpdateTimer != null)
+            if (_disposed)
+            {
+                return;
+            }
+
+            if (disposing)
             {
-                LibraryUpdateTimer.Dispose();
-                LibraryUpdateTimer = null;
+                _libraryUpdateTimer?.Dispose();
             }
+
+            _libraryUpdateTimer = null;
+
+            _libraryManager.ItemAdded -= OnLibraryManagerItemAdded;
+            _appHost.HasPendingRestartChanged -= OnAppHostHasPendingRestartChanged;
+            _appHost.HasUpdateAvailableChanged -= OnAppHostHasUpdateAvailableChanged;
+            _activityManager.EntryCreated -= OnActivityManagerEntryCreated;
+
+            _disposed = true;
         }
     }
 }

+ 28 - 11
Emby.Notifications/NotificationManager.cs

@@ -16,20 +16,32 @@ using Microsoft.Extensions.Logging;
 
 namespace Emby.Notifications
 {
+    /// <summary>
+    /// NotificationManager class.
+    /// </summary>
     public class NotificationManager : INotificationManager
     {
         private readonly ILogger _logger;
         private readonly IUserManager _userManager;
         private readonly IServerConfigurationManager _config;
 
-        private INotificationService[] _services;
-        private INotificationTypeFactory[] _typeFactories;
-
-        public NotificationManager(ILoggerFactory loggerFactory, IUserManager userManager, IServerConfigurationManager config)
+        private INotificationService[] _services = Array.Empty<INotificationService>();
+        private INotificationTypeFactory[] _typeFactories = Array.Empty<INotificationTypeFactory>();
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="NotificationManager" /> class.
+        /// </summary>
+        /// <param name="logger">The logger.</param>
+        /// <param name="userManager">the user manager.</param>
+        /// <param name="config">The server configuration manager.</param>
+        public NotificationManager(
+            ILogger<NotificationManager> logger,
+            IUserManager userManager,
+            IServerConfigurationManager config)
         {
+            _logger = logger;
             _userManager = userManager;
             _config = config;
-            _logger = loggerFactory.CreateLogger(GetType().Name);
         }
 
         private NotificationOptions GetConfiguration()
@@ -37,12 +49,14 @@ namespace Emby.Notifications
             return _config.GetConfiguration<NotificationOptions>("notifications");
         }
 
+        /// <inheritdoc />
         public Task SendNotification(NotificationRequest request, CancellationToken cancellationToken)
         {
             return SendNotification(request, null, cancellationToken);
         }
 
-        public Task SendNotification(NotificationRequest request, BaseItem relatedItem, CancellationToken cancellationToken)
+        /// <inheritdoc />
+        public Task SendNotification(NotificationRequest request, BaseItem? relatedItem, CancellationToken cancellationToken)
         {
             var notificationType = request.NotificationType;
 
@@ -64,7 +78,8 @@ namespace Emby.Notifications
             return Task.WhenAll(tasks);
         }
 
-        private Task SendNotification(NotificationRequest request,
+        private Task SendNotification(
+            NotificationRequest request,
             INotificationService service,
             IEnumerable<User> users,
             string title,
@@ -79,7 +94,7 @@ namespace Emby.Notifications
             return Task.WhenAll(tasks);
         }
 
-        private IEnumerable<Guid> GetUserIds(NotificationRequest request, NotificationOption options)
+        private IEnumerable<Guid> GetUserIds(NotificationRequest request, NotificationOption? options)
         {
             if (request.SendToUserMode.HasValue)
             {
@@ -109,7 +124,8 @@ namespace Emby.Notifications
             return request.UserIds;
         }
 
-        private async Task SendNotification(NotificationRequest request,
+        private async Task SendNotification(
+            NotificationRequest request,
             INotificationService service,
             string title,
             string description,
@@ -161,12 +177,14 @@ namespace Emby.Notifications
             return GetConfiguration().IsServiceEnabled(service.Name, notificationType);
         }
 
+        /// <inheritdoc />
         public void AddParts(IEnumerable<INotificationService> services, IEnumerable<INotificationTypeFactory> notificationTypeFactories)
         {
             _services = services.ToArray();
             _typeFactories = notificationTypeFactories.ToArray();
         }
 
+        /// <inheritdoc />
         public List<NotificationTypeInfo> GetNotificationTypes()
         {
             var list = _typeFactories.Select(i =>
@@ -180,7 +198,6 @@ namespace Emby.Notifications
                     _logger.LogError(ex, "Error in GetNotificationTypes");
                     return new List<NotificationTypeInfo>();
                 }
-
             }).SelectMany(i => i).ToList();
 
             var config = GetConfiguration();
@@ -193,13 +210,13 @@ namespace Emby.Notifications
             return list;
         }
 
+        /// <inheritdoc />
         public IEnumerable<NameIdPair> GetNotificationServices()
         {
             return _services.Select(i => new NameIdPair
             {
                 Name = i.Name,
                 Id = i.Name.GetMD5().ToString("N", CultureInfo.InvariantCulture)
-
             }).OrderBy(i => i.Name);
         }
     }

+ 1 - 0
Emby.Photos/Emby.Photos.csproj

@@ -18,6 +18,7 @@
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <Nullable>enable</Nullable>
   </PropertyGroup>
 
   <!-- Code analysers-->