From f4a160cb2902a37df8b0fabd83f1659119136af8 Mon Sep 17 00:00:00 2001 From: Bogdan Date: Mon, 13 Apr 2026 11:10:34 +0300 Subject: [PATCH] TypedResults for API v5 endpoints --- src/NzbDrone.Host/Startup.cs | 5 +++ src/Sonarr.Api.V3/Health/HealthController.cs | 3 +- .../Indexers/ReleaseControllerBase.cs | 3 +- .../LanguageProfileSchemaController.cs | 3 +- src/Sonarr.Api.V3/Queue/QueueController.cs | 3 +- .../Queue/QueueDetailsController.cs | 3 +- .../Queue/QueueStatusController.cs | 3 +- src/Sonarr.Api.V3/Series/SeriesController.cs | 3 +- .../Blocklist/BlocklistController.cs | 14 +++--- .../Calendar/CalendarController.cs | 6 ++- .../Calendar/CalendarFeedController.cs | 6 ++- .../Commands/CommandController.cs | 16 ++++--- .../Connections/ConnectionController.cs | 5 ++- .../CustomFilters/CustomFilterController.cs | 18 ++++---- .../DiskSpace/DiskSpaceController.cs | 6 ++- .../EpisodeFiles/EpisodeFileController.cs | 28 +++++++----- .../Episodes/EpisodeController.cs | 20 +++++---- .../Episodes/RenameEpisodeController.cs | 12 ++--- .../FileSystem/FileSystemController.cs | 20 +++++---- src/Sonarr.Api.V5/Health/HealthController.cs | 8 ++-- .../History/HistoryController.cs | 32 +++++++------- .../ImportListExclusionController.cs | 22 +++++----- .../Indexers/IndexerFlagController.cs | 8 ++-- .../Localization/LanguageController.cs | 6 ++- .../Localization/LocalizationController.cs | 14 +++--- src/Sonarr.Api.V5/Logs/LogController.cs | 8 ++-- .../Logs/LogFileControllerBase.cs | 12 ++--- .../ManualImport/ManualImportController.cs | 20 +++++---- .../Metadata/MetadataController.cs | 5 ++- src/Sonarr.Api.V5/Parse/ParseController.cs | 20 +++++---- .../Quality/QualityProfileController.cs | 18 ++++---- .../Quality/QualityProfileSchemaController.cs | 6 ++- .../Release/ReleaseProfileController.cs | 18 ++++---- .../Provider/ProviderControllerBase.cs | 44 ++++++++++--------- .../Qualities/QualityDefinitionController.cs | 15 ++++--- .../Queue/QueueActionController.cs | 10 +++-- src/Sonarr.Api.V5/Queue/QueueController.cs | 18 ++++---- .../Queue/QueueDetailsController.cs | 14 +++--- .../Queue/QueueStatusController.cs | 3 +- .../Release/ReleaseController.cs | 16 ++++--- .../Release/ReleasePushController.cs | 6 ++- .../RemotePathMappingController.cs | 18 ++++---- .../RootFolders/RootFolderController.cs | 14 +++--- .../SeasonPass/SeasonPassController.cs | 6 ++- src/Sonarr.Api.V5/Series/SeriesController.cs | 32 +++++++------- .../Series/SeriesEditorController.cs | 10 +++-- .../Series/SeriesFolderController.cs | 8 ++-- .../Series/SeriesImportController.cs | 6 ++- .../Series/SeriesLookupController.cs | 6 ++- .../Settings/GeneralSettingsController.cs | 4 +- .../Settings/NamingSettingsController.cs | 17 ++++--- .../Settings/SettingsController.cs | 23 +++++----- .../System/Backup/BackupController.cs | 22 +++++----- src/Sonarr.Api.V5/System/SystemController.cs | 24 +++++----- .../System/Tasks/TaskController.cs | 8 ++-- src/Sonarr.Api.V5/Tags/TagController.cs | 18 +++++--- .../Tags/TagDetailsController.cs | 6 ++- src/Sonarr.Api.V5/Update/UpdateController.cs | 8 ++-- src/Sonarr.Api.V5/Wanted/CutoffController.cs | 6 ++- src/Sonarr.Api.V5/Wanted/MissingController.cs | 6 ++- src/Sonarr.Http/REST/RestController.cs | 22 ++++++++-- 61 files changed, 447 insertions(+), 317 deletions(-) diff --git a/src/NzbDrone.Host/Startup.cs b/src/NzbDrone.Host/Startup.cs index 85b1bb55e..51bb431ff 100644 --- a/src/NzbDrone.Host/Startup.cs +++ b/src/NzbDrone.Host/Startup.cs @@ -108,6 +108,11 @@ public void ConfigureServices(IServiceCollection services) }) .AddControllersAsServices(); + services.ConfigureHttpJsonOptions(options => + { + STJson.ApplySerializerSettings(options.SerializerOptions); + }); + services.AddSwaggerGen(c => { c.SwaggerDoc("v3", new OpenApiInfo diff --git a/src/Sonarr.Api.V3/Health/HealthController.cs b/src/Sonarr.Api.V3/Health/HealthController.cs index fe1fd954c..10fbadcc0 100644 --- a/src/Sonarr.Api.V3/Health/HealthController.cs +++ b/src/Sonarr.Api.V3/Health/HealthController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.HealthCheck; @@ -23,7 +24,7 @@ public HealthController(IBroadcastSignalRMessage signalRBroadcaster, IHealthChec } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } diff --git a/src/Sonarr.Api.V3/Indexers/ReleaseControllerBase.cs b/src/Sonarr.Api.V3/Indexers/ReleaseControllerBase.cs index e08475952..78e30a989 100644 --- a/src/Sonarr.Api.V3/Indexers/ReleaseControllerBase.cs +++ b/src/Sonarr.Api.V3/Indexers/ReleaseControllerBase.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Profiles.Qualities; @@ -17,7 +18,7 @@ public ReleaseControllerBase(IQualityProfileService qualityProfileService) } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } diff --git a/src/Sonarr.Api.V3/Profiles/Languages/LanguageProfileSchemaController.cs b/src/Sonarr.Api.V3/Profiles/Languages/LanguageProfileSchemaController.cs index ad5a32704..bccee3469 100644 --- a/src/Sonarr.Api.V3/Profiles/Languages/LanguageProfileSchemaController.cs +++ b/src/Sonarr.Api.V3/Profiles/Languages/LanguageProfileSchemaController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Languages; using Sonarr.Http; @@ -33,7 +34,7 @@ public LanguageProfileResource GetSchema() } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } diff --git a/src/Sonarr.Api.V3/Queue/QueueController.cs b/src/Sonarr.Api.V3/Queue/QueueController.cs index 454b497fc..684d29a10 100644 --- a/src/Sonarr.Api.V3/Queue/QueueController.cs +++ b/src/Sonarr.Api.V3/Queue/QueueController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Blocklisting; @@ -61,7 +62,7 @@ public QueueController(IBroadcastSignalRMessage broadcastSignalRMessage, } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } diff --git a/src/Sonarr.Api.V3/Queue/QueueDetailsController.cs b/src/Sonarr.Api.V3/Queue/QueueDetailsController.cs index 152b4b733..d4d28afd9 100644 --- a/src/Sonarr.Api.V3/Queue/QueueDetailsController.cs +++ b/src/Sonarr.Api.V3/Queue/QueueDetailsController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Download.Pending; @@ -28,7 +29,7 @@ public QueueDetailsController(IBroadcastSignalRMessage broadcastSignalRMessage, } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } diff --git a/src/Sonarr.Api.V3/Queue/QueueStatusController.cs b/src/Sonarr.Api.V3/Queue/QueueStatusController.cs index 57d87d107..fa43ad189 100644 --- a/src/Sonarr.Api.V3/Queue/QueueStatusController.cs +++ b/src/Sonarr.Api.V3/Queue/QueueStatusController.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.TPL; using NzbDrone.Core.Datastore.Events; @@ -31,7 +32,7 @@ public QueueStatusController(IBroadcastSignalRMessage broadcastSignalRMessage, I } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } diff --git a/src/Sonarr.Api.V3/Series/SeriesController.cs b/src/Sonarr.Api.V3/Series/SeriesController.cs index 8f155b9ff..04a6abe82 100644 --- a/src/Sonarr.Api.V3/Series/SeriesController.cs +++ b/src/Sonarr.Api.V3/Series/SeriesController.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using FluentValidation; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.DataAugmentation.Scene; @@ -130,7 +131,7 @@ public List AllSeries(int? tvdbId, bool includeSeasonImages = fa } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } diff --git a/src/Sonarr.Api.V5/Blocklist/BlocklistController.cs b/src/Sonarr.Api.V5/Blocklist/BlocklistController.cs index 22b27d3cf..f4895e6b2 100644 --- a/src/Sonarr.Api.V5/Blocklist/BlocklistController.cs +++ b/src/Sonarr.Api.V5/Blocklist/BlocklistController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Blocklisting; using NzbDrone.Core.CustomFormats; @@ -24,7 +26,7 @@ public BlocklistController(IBlocklistService blocklistService, [HttpGet] [Produces("application/json")] - public PagingResource GetBlocklist([FromQuery] PagingRequestResource paging, [FromQuery] int[]? seriesIds = null, [FromQuery] DownloadProtocol[]? protocols = null) + public Ok> GetBlocklist([FromQuery] PagingRequestResource paging, [FromQuery] int[]? seriesIds = null, [FromQuery] DownloadProtocol[]? protocols = null) { var pagingResource = new PagingResource(paging); var pagingSpec = pagingResource.MapToPagingSpec( @@ -48,23 +50,23 @@ public PagingResource GetBlocklist([FromQuery] PagingRequestR pagingSpec.FilterExpressions.Add(b => protocols.Contains(b.Protocol)); } - return pagingSpec.ApplyToPage(b => _blocklistService.Paged(pagingSpec), b => BlocklistResourceMapper.MapToResource(b, _formatCalculator)); + return TypedResults.Ok(pagingSpec.ApplyToPage(b => _blocklistService.Paged(pagingSpec), b => BlocklistResourceMapper.MapToResource(b, _formatCalculator))); } [RestDeleteById] - public ActionResult DeleteBlocklist(int id) + public NoContent DeleteBlocklist(int id) { _blocklistService.Delete(id); - return NoContent(); + return TypedResults.NoContent(); } [HttpDelete("bulk")] [Produces("application/json")] - public ActionResult Remove([FromBody] BlocklistBulkResource resource) + public NoContent Remove([FromBody] BlocklistBulkResource resource) { _blocklistService.Delete(resource.Ids); - return NoContent(); + return TypedResults.NoContent(); } } diff --git a/src/Sonarr.Api.V5/Calendar/CalendarController.cs b/src/Sonarr.Api.V5/Calendar/CalendarController.cs index 3eb37d4da..003c60d83 100644 --- a/src/Sonarr.Api.V5/Calendar/CalendarController.cs +++ b/src/Sonarr.Api.V5/Calendar/CalendarController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.CustomFormats; @@ -28,7 +30,7 @@ public CalendarController(IBroadcastSignalRMessage signalR, [HttpGet] [Produces("application/json")] - public List GetCalendar(DateTime? start, DateTime? end, bool includeUnmonitored = false, bool includeSpecials = true, string tags = "", [FromQuery] CalendarSubresource[]? includeSubresources = null) + public Ok> GetCalendar(DateTime? start, DateTime? end, bool includeUnmonitored = false, bool includeSpecials = true, string tags = "", [FromQuery] CalendarSubresource[]? includeSubresources = null) { var startUse = start ?? DateTime.Today; var endUse = end ?? DateTime.Today.AddDays(2); @@ -65,7 +67,7 @@ public List GetCalendar(DateTime? start, DateTime? end, bool in var resources = MapToResource(result, includeSeries, includeEpisodeFile, includeEpisodeImages); - return resources.OrderBy(e => e.AirDateUtc).ToList(); + return TypedResults.Ok(resources.OrderBy(e => e.AirDateUtc).ToList()); } } } diff --git a/src/Sonarr.Api.V5/Calendar/CalendarFeedController.cs b/src/Sonarr.Api.V5/Calendar/CalendarFeedController.cs index e1f1ec083..c8c0b4095 100644 --- a/src/Sonarr.Api.V5/Calendar/CalendarFeedController.cs +++ b/src/Sonarr.Api.V5/Calendar/CalendarFeedController.cs @@ -2,6 +2,8 @@ using Ical.Net.CalendarComponents; using Ical.Net.DataTypes; using Ical.Net.Serialization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Tags; @@ -25,7 +27,7 @@ public CalendarFeedController(IEpisodeService episodeService, ISeriesService ser } [HttpGet("Sonarr.ics")] - public IActionResult GetCalendarFeed(int pastDays = 7, int futureDays = 28, string tags = "", bool unmonitored = false, bool premieresOnly = false, bool asAllDay = false, bool includeSpecials = true) + public ContentHttpResult GetCalendarFeed(int pastDays = 7, int futureDays = 28, string tags = "", bool unmonitored = false, bool premieresOnly = false, bool asAllDay = false, bool includeSpecials = true) { var start = DateTime.Today.AddDays(-pastDays); var end = DateTime.Today.AddDays(futureDays); @@ -96,6 +98,6 @@ public IActionResult GetCalendarFeed(int pastDays = 7, int futureDays = 28, stri var serializer = (IStringSerializer)new SerializerFactory().Build(calendar.GetType(), new SerializationContext()); var icalendar = serializer.SerializeToString(calendar); - return Content(icalendar, "text/calendar"); + return TypedResults.Content(icalendar, "text/calendar"); } } diff --git a/src/Sonarr.Api.V5/Commands/CommandController.cs b/src/Sonarr.Api.V5/Commands/CommandController.cs index ebb0b3beb..2880f3066 100644 --- a/src/Sonarr.Api.V5/Commands/CommandController.cs +++ b/src/Sonarr.Api.V5/Commands/CommandController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Composition; using NzbDrone.Common.Serializer; @@ -46,7 +48,7 @@ protected override CommandResource GetResourceById(int id) [RestPostById] [Consumes("application/json")] [Produces("application/json")] - public ActionResult StartCommand([FromBody] CommandResource commandResource) + public Results, NotFound> StartCommand([FromBody] CommandResource commandResource) { var commandType = _knownTypes.GetImplementations(typeof(Command)) @@ -70,24 +72,26 @@ public ActionResult StartCommand([FromBody] CommandResource com var trackedCommand = _commandQueueManager.Push(command, commandResource.Priority, CommandTrigger.Manual); - return Created(trackedCommand.Id); + return TypedCreated(trackedCommand.Id); } } [HttpGet] [Produces("application/json")] - public List GetStartedCommands() + public Ok> GetStartedCommands() { - return _commandQueueManager.All() + return TypedResults.Ok(_commandQueueManager.All() .OrderBy(c => c.Status, _commandPriorityComparer) .ThenByDescending(c => c.Priority) - .ToResource(); + .ToResource()); } [RestDeleteById] - public void CancelCommand(int id) + public NoContent CancelCommand(int id) { _commandQueueManager.Cancel(id); + + return TypedResults.NoContent(); } [NonAction] diff --git a/src/Sonarr.Api.V5/Connections/ConnectionController.cs b/src/Sonarr.Api.V5/Connections/ConnectionController.cs index 5349a6d8b..c5dc47d1f 100644 --- a/src/Sonarr.Api.V5/Connections/ConnectionController.cs +++ b/src/Sonarr.Api.V5/Connections/ConnectionController.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Notifications; using NzbDrone.SignalR; @@ -18,13 +19,13 @@ public ConnectionController(IBroadcastSignalRMessage signalRBroadcaster, Notific } [NonAction] - public override ActionResult UpdateProvider([FromBody] ConnectionBulkResource providerResource) + public override Results>, BadRequest> UpdateProvider([FromBody] ConnectionBulkResource providerResource) { throw new NotImplementedException(); } [NonAction] - public override ActionResult DeleteProviders([FromBody] ConnectionBulkResource resource) + public override NoContent DeleteProviders([FromBody] ConnectionBulkResource resource) { throw new NotImplementedException(); } diff --git a/src/Sonarr.Api.V5/CustomFilters/CustomFilterController.cs b/src/Sonarr.Api.V5/CustomFilters/CustomFilterController.cs index 61a267b68..6cfe21944 100644 --- a/src/Sonarr.Api.V5/CustomFilters/CustomFilterController.cs +++ b/src/Sonarr.Api.V5/CustomFilters/CustomFilterController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.CustomFilters; using Sonarr.Http; @@ -23,33 +25,33 @@ protected override CustomFilterResource GetResourceById(int id) [HttpGet] [Produces("application/json")] - public List GetCustomFilters() + public Ok> GetCustomFilters() { - return _customFilterService.All().ToResource(); + return TypedResults.Ok(_customFilterService.All().ToResource()); } [RestPostById] [Consumes("application/json")] - public ActionResult AddCustomFilter([FromBody] CustomFilterResource resource) + public Results, NotFound> AddCustomFilter([FromBody] CustomFilterResource resource) { var customFilter = _customFilterService.Add(resource.ToModel()); - return Created(customFilter.Id); + return TypedCreated(customFilter.Id); } [RestPutById] [Consumes("application/json")] - public ActionResult UpdateCustomFilter([FromBody] CustomFilterResource resource) + public Results, NotFound> UpdateCustomFilter([FromBody] CustomFilterResource resource) { _customFilterService.Update(resource.ToModel()); - return Accepted(resource.Id); + return TypedAccepted(resource.Id); } [RestDeleteById] - public ActionResult DeleteCustomResource(int id) + public NoContent DeleteCustomResource(int id) { _customFilterService.Delete(id); - return NoContent(); + return TypedResults.NoContent(); } } diff --git a/src/Sonarr.Api.V5/DiskSpace/DiskSpaceController.cs b/src/Sonarr.Api.V5/DiskSpace/DiskSpaceController.cs index b0a1a9abe..04cc40421 100644 --- a/src/Sonarr.Api.V5/DiskSpace/DiskSpaceController.cs +++ b/src/Sonarr.Api.V5/DiskSpace/DiskSpaceController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.DiskSpace; using Sonarr.Http; @@ -16,8 +18,8 @@ public DiskSpaceController(IDiskSpaceService diskSpaceService) [HttpGet] [Produces("application/json")] - public List GetFreeSpace() + public Ok> GetFreeSpace() { - return _diskSpaceService.GetFreeSpace().ConvertAll(DiskSpaceResourceMapper.MapToResource); + return TypedResults.Ok(_diskSpaceService.GetFreeSpace().ConvertAll(DiskSpaceResourceMapper.MapToResource)); } } diff --git a/src/Sonarr.Api.V5/EpisodeFiles/EpisodeFileController.cs b/src/Sonarr.Api.V5/EpisodeFiles/EpisodeFileController.cs index 1bfef1f4c..921b91682 100644 --- a/src/Sonarr.Api.V5/EpisodeFiles/EpisodeFileController.cs +++ b/src/Sonarr.Api.V5/EpisodeFiles/EpisodeFileController.cs @@ -1,4 +1,6 @@ using System.Net; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.Datastore.Events; @@ -57,7 +59,7 @@ protected override EpisodeFileResource GetResourceById(int id) [HttpGet] [Produces("application/json")] - public List GetEpisodeFiles(int? seriesId, [FromQuery] List? episodeFileIds) + public Results>, BadRequest> GetEpisodeFiles(int? seriesId, [FromQuery] List? episodeFileIds) { if (!seriesId.HasValue && episodeFileIds?.Any() == false) { @@ -71,25 +73,25 @@ public List GetEpisodeFiles(int? seriesId, [FromQuery] List if (files == null) { - return new List(); + return TypedResults.Ok(new List()); } - return files.ConvertAll(e => e.ToResource(series, _upgradableSpecification, _formatCalculator)); + return TypedResults.Ok(files.ConvertAll(e => e.ToResource(series, _upgradableSpecification, _formatCalculator))); } else { var episodeFiles = _mediaFileService.Get(episodeFileIds); - return episodeFiles.GroupBy(e => e.SeriesId) + return TypedResults.Ok(episodeFiles.GroupBy(e => e.SeriesId) .SelectMany(f => f.ToList() .ConvertAll(e => e.ToResource(_seriesService.GetSeries(f.Key), _upgradableSpecification, _formatCalculator))) - .ToList(); + .ToList()); } } [RestPutById] [Consumes("application/json")] - public ActionResult SetQuality([FromBody] EpisodeFileResource episodeFileResource) + public Results, NotFound> SetQuality([FromBody] EpisodeFileResource episodeFileResource) { var episodeFile = _mediaFileService.Get(episodeFileResource.Id); episodeFile.Quality = episodeFileResource.Quality; @@ -105,11 +107,11 @@ public ActionResult SetQuality([FromBody] EpisodeFileResour } _mediaFileService.Update(episodeFile); - return Accepted(episodeFile.Id); + return TypedAccepted(episodeFile.Id); } [RestDeleteById] - public void DeleteEpisodeFile(int id) + public Results DeleteEpisodeFile(int id) { var episodeFile = _mediaFileService.Get(id); @@ -121,11 +123,13 @@ public void DeleteEpisodeFile(int id) var series = _seriesService.GetSeries(episodeFile.SeriesId); _mediaFileDeletionService.DeleteEpisodeFile(series, episodeFile); + + return TypedResults.NoContent(); } [HttpDelete("bulk")] [Consumes("application/json")] - public object DeleteEpisodeFiles([FromBody] EpisodeFileListResource resource) + public NoContent DeleteEpisodeFiles([FromBody] EpisodeFileListResource resource) { var episodeFiles = _mediaFileService.GetFiles(resource.EpisodeFileIds); var series = _seriesService.GetSeries(episodeFiles.First().SeriesId); @@ -135,12 +139,12 @@ public object DeleteEpisodeFiles([FromBody] EpisodeFileListResource resource) _mediaFileDeletionService.DeleteEpisodeFile(series, episodeFile); } - return NoContent(); + return TypedResults.NoContent(); } [HttpPut("bulk")] [Consumes("application/json")] - public object SetPropertiesBulk([FromBody] List resources) + public Ok> SetPropertiesBulk([FromBody] List resources) { var episodeFiles = _mediaFileService.GetFiles(resources.Select(r => r.Id)); @@ -184,7 +188,7 @@ public object SetPropertiesBulk([FromBody] List resources) var series = _seriesService.GetSeries(episodeFiles.First().SeriesId); - return Accepted(episodeFiles.ConvertAll(f => f.ToResource(series, _upgradableSpecification, _formatCalculator))); + return TypedResults.Ok(episodeFiles.ConvertAll(f => f.ToResource(series, _upgradableSpecification, _formatCalculator))); } [NonAction] diff --git a/src/Sonarr.Api.V5/Episodes/EpisodeController.cs b/src/Sonarr.Api.V5/Episodes/EpisodeController.cs index bb6e105ef..367bd1aca 100644 --- a/src/Sonarr.Api.V5/Episodes/EpisodeController.cs +++ b/src/Sonarr.Api.V5/Episodes/EpisodeController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.DecisionEngine.Specifications; @@ -23,7 +25,7 @@ public EpisodeController(ISeriesService seriesService, [HttpGet] [Produces("application/json")] - public List GetEpisodes(int? seriesId, int? seasonNumber, [FromQuery]List episodeIds, int? episodeFileId, [FromQuery] EpisodeSubresource[]? includeSubresources = null) + public Results>, BadRequest> GetEpisodes(int? seriesId, int? seasonNumber, [FromQuery]List episodeIds, int? episodeFileId, [FromQuery] EpisodeSubresource[]? includeSubresources = null) { var includeSeries = includeSubresources.Contains(EpisodeSubresource.Series); var includeEpisodeFile = includeSubresources.Contains(EpisodeSubresource.EpisodeFile); @@ -33,18 +35,18 @@ public List GetEpisodes(int? seriesId, int? seasonNumber, [From { if (seasonNumber.HasValue) { - return MapToResource(_episodeService.GetEpisodesBySeason(seriesId.Value, seasonNumber.Value), includeSeries, includeEpisodeFile, includeImages); + return TypedResults.Ok(MapToResource(_episodeService.GetEpisodesBySeason(seriesId.Value, seasonNumber.Value), includeSeries, includeEpisodeFile, includeImages)); } - return MapToResource(_episodeService.GetEpisodeBySeries(seriesId.Value), includeSeries, includeEpisodeFile, includeImages); + return TypedResults.Ok(MapToResource(_episodeService.GetEpisodeBySeries(seriesId.Value), includeSeries, includeEpisodeFile, includeImages)); } else if (episodeIds.Any()) { - return MapToResource(_episodeService.GetEpisodes(episodeIds), includeSeries, includeEpisodeFile, includeImages); + return TypedResults.Ok(MapToResource(_episodeService.GetEpisodes(episodeIds), includeSeries, includeEpisodeFile, includeImages)); } else if (episodeFileId.HasValue) { - return MapToResource(_episodeService.GetEpisodesByFileId(episodeFileId.Value), includeSeries, includeEpisodeFile, includeImages); + return TypedResults.Ok(MapToResource(_episodeService.GetEpisodesByFileId(episodeFileId.Value), includeSeries, includeEpisodeFile, includeImages)); } throw new BadRequestException("seriesId or episodeIds must be provided"); @@ -52,18 +54,18 @@ public List GetEpisodes(int? seriesId, int? seasonNumber, [From [RestPutById] [Consumes("application/json")] - public ActionResult SetEpisodeMonitored([FromRoute] int id, [FromBody] EpisodeResource resource) + public Ok SetEpisodeMonitored([FromRoute] int id, [FromBody] EpisodeResource resource) { _episodeService.SetEpisodeMonitored(id, resource.Monitored); resource = MapToResource(_episodeService.GetEpisode(id), false, false, false); - return Accepted(resource); + return TypedResults.Ok(resource); } [HttpPut("monitor")] [Consumes("application/json")] - public IActionResult SetEpisodesMonitored([FromBody] EpisodesMonitoredResource resource, [FromQuery] EpisodeSubresource[]? includeSubresources = null) + public Ok> SetEpisodesMonitored([FromBody] EpisodesMonitoredResource resource, [FromQuery] EpisodeSubresource[]? includeSubresources = null) { var includeImages = includeSubresources.Contains(EpisodeSubresource.Images); @@ -78,6 +80,6 @@ public IActionResult SetEpisodesMonitored([FromBody] EpisodesMonitoredResource r var resources = MapToResource(_episodeService.GetEpisodes(resource.EpisodeIds), false, false, includeImages); - return Accepted(resources); + return TypedResults.Ok(resources); } } diff --git a/src/Sonarr.Api.V5/Episodes/RenameEpisodeController.cs b/src/Sonarr.Api.V5/Episodes/RenameEpisodeController.cs index 05494a33b..7c8e5ccdb 100644 --- a/src/Sonarr.Api.V5/Episodes/RenameEpisodeController.cs +++ b/src/Sonarr.Api.V5/Episodes/RenameEpisodeController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.MediaFiles; using Sonarr.Http; @@ -17,19 +19,19 @@ public RenameEpisodeController(IRenameEpisodeFileService renameEpisodeFileServic [HttpGet] [Produces("application/json")] - public List GetEpisodes(int seriesId, int? seasonNumber) + public Ok> GetEpisodes(int seriesId, int? seasonNumber) { if (seasonNumber.HasValue) { - return _renameEpisodeFileService.GetRenamePreviews(seriesId, seasonNumber.Value).ToResource(); + return TypedResults.Ok(_renameEpisodeFileService.GetRenamePreviews(seriesId, seasonNumber.Value).ToResource()); } - return _renameEpisodeFileService.GetRenamePreviews(seriesId).ToResource(); + return TypedResults.Ok(_renameEpisodeFileService.GetRenamePreviews(seriesId).ToResource()); } [HttpGet("bulk")] [Produces("application/json")] - public List GetEpisodes([FromQuery] List seriesIds) + public Results>, BadRequest> GetEpisodes([FromQuery] List seriesIds) { if (seriesIds is { Count: 0 }) { @@ -41,6 +43,6 @@ public List GetEpisodes([FromQuery] List seriesIds) throw new BadRequestException("seriesIds must be positive integers"); } - return _renameEpisodeFileService.GetRenamePreviews(seriesIds).ToResource(); + return TypedResults.Ok(_renameEpisodeFileService.GetRenamePreviews(seriesIds).ToResource()); } } diff --git a/src/Sonarr.Api.V5/FileSystem/FileSystemController.cs b/src/Sonarr.Api.V5/FileSystem/FileSystemController.cs index ad54be2f7..820b41810 100644 --- a/src/Sonarr.Api.V5/FileSystem/FileSystemController.cs +++ b/src/Sonarr.Api.V5/FileSystem/FileSystemController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; @@ -24,38 +26,38 @@ public FileSystemController(IFileSystemLookupService fileSystemLookupService, [HttpGet] [Produces("application/json")] - public IActionResult GetContents(string? path, bool includeFiles = false, bool allowFoldersWithoutTrailingSlashes = false) + public Ok GetContents(string? path, bool includeFiles = false, bool allowFoldersWithoutTrailingSlashes = false) { - return Ok(_fileSystemLookupService.LookupContents(path, includeFiles, allowFoldersWithoutTrailingSlashes)); + return TypedResults.Ok(_fileSystemLookupService.LookupContents(path, includeFiles, allowFoldersWithoutTrailingSlashes)); } [HttpGet("type")] [Produces("application/json")] - public object GetEntityType(string path) + public Ok GetEntityType(string path) { if (_diskProvider.FileExists(path)) { - return new { type = "file" }; + return TypedResults.Ok((object)new { type = "file" }); } // Return folder even if it doesn't exist on disk to avoid leaking anything from the UI about the underlying system - return new { type = "folder" }; + return TypedResults.Ok((object)new { type = "folder" }); } [HttpGet("mediafiles")] [Produces("application/json")] - public object GetMediaFiles(string path) + public Ok> GetMediaFiles(string path) { if (!_diskProvider.FolderExists(path)) { - return Array.Empty(); + return TypedResults.Ok(Enumerable.Empty()); } - return _diskScanService.GetVideoFiles(path).Select(f => new + return TypedResults.Ok(_diskScanService.GetVideoFiles(path).Select(object (f) => new { Path = f, RelativePath = path.GetRelativePath(f), Name = Path.GetFileName(f) - }); + })); } } diff --git a/src/Sonarr.Api.V5/Health/HealthController.cs b/src/Sonarr.Api.V5/Health/HealthController.cs index f7912fe7d..4610a1c5e 100644 --- a/src/Sonarr.Api.V5/Health/HealthController.cs +++ b/src/Sonarr.Api.V5/Health/HealthController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.HealthCheck; @@ -21,7 +23,7 @@ public HealthController(IBroadcastSignalRMessage signalRBroadcaster, IHealthChec } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } @@ -33,9 +35,9 @@ protected override HealthResource GetResourceById(int id) [HttpGet] [Produces("application/json")] - public List GetHealth() + public Ok> GetHealth() { - return _healthCheckService.Results().ToResource(); + return TypedResults.Ok(_healthCheckService.Results().ToResource()); } [NonAction] diff --git a/src/Sonarr.Api.V5/History/HistoryController.cs b/src/Sonarr.Api.V5/History/HistoryController.cs index 38d805009..f11b960d0 100644 --- a/src/Sonarr.Api.V5/History/HistoryController.cs +++ b/src/Sonarr.Api.V5/History/HistoryController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.CustomFormats; @@ -62,7 +64,7 @@ protected HistoryResource MapToResource(EpisodeHistory model, bool includeSeries [HttpGet] [Produces("application/json")] - public PagingResource GetHistory([FromQuery] PagingRequestResource paging, [FromQuery(Name = "eventType")] int[]? eventTypes, int? episodeId, string? downloadId, [FromQuery] int[]? seriesIds = null, [FromQuery] int[]? languages = null, [FromQuery] int[]? quality = null, [FromQuery] HistorySubresource[]? includeSubresources = null) + public Ok> GetHistory([FromQuery] PagingRequestResource paging, [FromQuery(Name = "eventType")] int[]? eventTypes, int? episodeId, string? downloadId, [FromQuery] int[]? seriesIds = null, [FromQuery] int[]? languages = null, [FromQuery] int[]? quality = null, [FromQuery] HistorySubresource[]? includeSubresources = null) { var pagingResource = new PagingResource(paging); var pagingSpec = pagingResource.MapToPagingSpec( @@ -97,74 +99,74 @@ public PagingResource GetHistory([FromQuery] PagingRequestResou var includeSeries = includeSubresources.Contains(HistorySubresource.Series); var includeEpisode = includeSubresources.Contains(HistorySubresource.Episode); - return pagingSpec.ApplyToPage(h => _historyService.Paged(pagingSpec, languages, quality), h => MapToResource(h, includeSeries, includeEpisode)); + return TypedResults.Ok(pagingSpec.ApplyToPage(h => _historyService.Paged(pagingSpec, languages, quality), h => MapToResource(h, includeSeries, includeEpisode))); } [HttpGet("since")] [Produces("application/json")] - public List GetHistorySince(DateTime date, EpisodeHistoryEventType? eventType = null, [FromQuery] HistorySubresource[]? includeSubresources = null) + public Ok> GetHistorySince(DateTime date, EpisodeHistoryEventType? eventType = null, [FromQuery] HistorySubresource[]? includeSubresources = null) { var includeSeries = includeSubresources.Contains(HistorySubresource.Series); var includeEpisode = includeSubresources.Contains(HistorySubresource.Episode); - return _historyService.Since(date, eventType).Select(h => MapToResource(h, includeSeries, includeEpisode)).ToList(); + return TypedResults.Ok(_historyService.Since(date, eventType).Select(h => MapToResource(h, includeSeries, includeEpisode)).ToList()); } [HttpGet("series")] [Produces("application/json")] - public List GetSeriesHistory(int seriesId, EpisodeHistoryEventType? eventType = null, [FromQuery] HistorySubresource[]? includeSubresources = null) + public Ok> GetSeriesHistory(int seriesId, EpisodeHistoryEventType? eventType = null, [FromQuery] HistorySubresource[]? includeSubresources = null) { var series = _seriesService.GetSeries(seriesId); var includeSeries = includeSubresources.Contains(HistorySubresource.Series); var includeEpisode = includeSubresources.Contains(HistorySubresource.Episode); - return _historyService.GetBySeries(seriesId, eventType).Select(h => + return TypedResults.Ok(_historyService.GetBySeries(seriesId, eventType).Select(h => { h.Series = series; return MapToResource(h, includeSeries, includeEpisode); - }).ToList(); + }).ToList()); } [HttpGet("season")] [Produces("application/json")] - public List GetSeasonHistory(int seriesId, int seasonNumber, EpisodeHistoryEventType? eventType = null, [FromQuery] HistorySubresource[]? includeSubresources = null) + public Ok> GetSeasonHistory(int seriesId, int seasonNumber, EpisodeHistoryEventType? eventType = null, [FromQuery] HistorySubresource[]? includeSubresources = null) { var series = _seriesService.GetSeries(seriesId); var includeSeries = includeSubresources.Contains(HistorySubresource.Series); var includeEpisode = includeSubresources.Contains(HistorySubresource.Episode); - return _historyService.GetBySeason(seriesId, seasonNumber, eventType).Select(h => + return TypedResults.Ok(_historyService.GetBySeason(seriesId, seasonNumber, eventType).Select(h => { h.Series = series; return MapToResource(h, includeSeries, includeEpisode); - }).ToList(); + }).ToList()); } [HttpGet("episode")] [Produces("application/json")] - public List GetEpisodeHistory(int episodeId, EpisodeHistoryEventType? eventType = null, [FromQuery] HistorySubresource[]? includeSubresources = null) + public Ok> GetEpisodeHistory(int episodeId, EpisodeHistoryEventType? eventType = null, [FromQuery] HistorySubresource[]? includeSubresources = null) { var episode = _episodeService.GetEpisode(episodeId); var series = _seriesService.GetSeries(episode.SeriesId); var includeSeries = includeSubresources.Contains(HistorySubresource.Series); var includeEpisode = includeSubresources.Contains(HistorySubresource.Episode); - return _historyService.GetByEpisode(episodeId, eventType) + return TypedResults.Ok(_historyService.GetByEpisode(episodeId, eventType) .Select(h => { h.Series = series; h.Episode = episode; return MapToResource(h, includeSeries, includeEpisode); - }).ToList(); + }).ToList()); } [HttpPost("failed/{id}")] - public ActionResult MarkAsFailed([FromRoute] int id) + public NoContent MarkAsFailed([FromRoute] int id) { _failedDownloadService.MarkAsFailed(id); - return NoContent(); + return TypedResults.NoContent(); } } diff --git a/src/Sonarr.Api.V5/ImportLists/ImportListExclusionController.cs b/src/Sonarr.Api.V5/ImportLists/ImportListExclusionController.cs index fd9ed5cbe..afae596c0 100644 --- a/src/Sonarr.Api.V5/ImportLists/ImportListExclusionController.cs +++ b/src/Sonarr.Api.V5/ImportLists/ImportListExclusionController.cs @@ -1,4 +1,6 @@ using FluentValidation; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Datastore; using NzbDrone.Core.ImportLists.Exclusions; @@ -33,7 +35,7 @@ protected override ImportListExclusionResource GetResourceById(int id) [HttpGet] [Produces("application/json")] - public PagingResource GetImportListExclusions([FromQuery] PagingRequestResource paging) + public Ok> GetImportListExclusions([FromQuery] PagingRequestResource paging) { var pagingResource = new PagingResource(paging); var pageSpec = pagingResource.MapToPagingSpec( @@ -46,41 +48,41 @@ public PagingResource GetImportListExclusions([From "id", SortDirection.Descending); - return pageSpec.ApplyToPage(_importListExclusionService.Paged, ImportListExclusionResourceMapper.ToResource); + return TypedResults.Ok(pageSpec.ApplyToPage(_importListExclusionService.Paged, ImportListExclusionResourceMapper.ToResource)); } [RestPostById] [Consumes("application/json")] - public ActionResult AddImportListExclusion([FromBody] ImportListExclusionResource resource) + public Results, NotFound> AddImportListExclusion([FromBody] ImportListExclusionResource resource) { var importListExclusion = _importListExclusionService.Add(resource.ToModel()); - return Created(importListExclusion.Id); + return TypedCreated(importListExclusion.Id); } [RestPutById] [Consumes("application/json")] - public ActionResult UpdateImportListExclusion([FromBody] ImportListExclusionResource resource) + public Results, NotFound> UpdateImportListExclusion([FromBody] ImportListExclusionResource resource) { _importListExclusionService.Update(resource.ToModel()); - return Accepted(resource.Id); + return TypedAccepted(resource.Id); } [RestDeleteById] - public ActionResult DeleteImportListExclusion(int id) + public NoContent DeleteImportListExclusion(int id) { _importListExclusionService.Delete(id); - return NoContent(); + return TypedResults.NoContent(); } [HttpDelete("bulk")] [Consumes("application/json")] - public ActionResult DeleteImportListExclusions([FromBody] ImportListExclusionBulkResource resource) + public NoContent DeleteImportListExclusions([FromBody] ImportListExclusionBulkResource resource) { _importListExclusionService.Delete(resource.Ids.ToList()); - return NoContent(); + return TypedResults.NoContent(); } } diff --git a/src/Sonarr.Api.V5/Indexers/IndexerFlagController.cs b/src/Sonarr.Api.V5/Indexers/IndexerFlagController.cs index 2424b98f4..79d874e0c 100644 --- a/src/Sonarr.Api.V5/Indexers/IndexerFlagController.cs +++ b/src/Sonarr.Api.V5/Indexers/IndexerFlagController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Parser.Model; using Sonarr.Http; @@ -8,12 +10,12 @@ namespace Sonarr.Api.V5.Indexers; public class IndexerFlagController : Controller { [HttpGet] - public List GetAll() + public Ok> GetAll() { - return Enum.GetValues(typeof(IndexerFlags)).Cast().Select(f => new IndexerFlagResource + return TypedResults.Ok(Enum.GetValues(typeof(IndexerFlags)).Cast().Select(f => new IndexerFlagResource { Id = (int)f, Name = f.ToString() - }).ToList(); + }).ToList()); } } diff --git a/src/Sonarr.Api.V5/Localization/LanguageController.cs b/src/Sonarr.Api.V5/Localization/LanguageController.cs index ea010585b..ddb5d188c 100644 --- a/src/Sonarr.Api.V5/Localization/LanguageController.cs +++ b/src/Sonarr.Api.V5/Localization/LanguageController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Languages; using Sonarr.Http; @@ -20,7 +22,7 @@ protected override LanguageResource GetResourceById(int id) } [HttpGet] - public List GetAll() + public Ok> GetAll() { var languageResources = Language.All.Select(l => new LanguageResource { @@ -30,6 +32,6 @@ public List GetAll() .OrderBy(l => l.Id > 0).ThenBy(l => l.Name) .ToList(); - return languageResources; + return TypedResults.Ok(languageResources); } } diff --git a/src/Sonarr.Api.V5/Localization/LocalizationController.cs b/src/Sonarr.Api.V5/Localization/LocalizationController.cs index 6e5c91eec..2f924efa8 100644 --- a/src/Sonarr.Api.V5/Localization/LocalizationController.cs +++ b/src/Sonarr.Api.V5/Localization/LocalizationController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Localization; using Sonarr.Http; @@ -17,25 +19,25 @@ public LocalizationController(ILocalizationService localizationService) protected override LocalizationResource GetResourceById(int id) { - return GetLocalization(); + return _localizationService.GetLocalizationDictionary().ToResource(); } [HttpGet] [Produces("application/json")] - public LocalizationResource GetLocalization() + public Ok GetLocalization() { - return _localizationService.GetLocalizationDictionary().ToResource(); + return TypedResults.Ok(GetResourceById(1)); } [HttpGet("language")] [Produces("application/json")] - public LocalizationLanguageResource GetLanguage() + public Ok GetLanguage() { var identifier = _localizationService.GetLanguageIdentifier(); - return new LocalizationLanguageResource + return TypedResults.Ok(new LocalizationLanguageResource { Identifier = identifier - }; + }); } } diff --git a/src/Sonarr.Api.V5/Logs/LogController.cs b/src/Sonarr.Api.V5/Logs/LogController.cs index 4f9a8e09a..757aec073 100644 --- a/src/Sonarr.Api.V5/Logs/LogController.cs +++ b/src/Sonarr.Api.V5/Logs/LogController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Configuration; @@ -21,11 +23,11 @@ public LogController(ILogService logService, IConfigFileProvider configFileProvi [HttpGet] [Produces("application/json")] - public PagingResource GetLogs([FromQuery] PagingRequestResource paging, string? level) + public Ok> GetLogs([FromQuery] PagingRequestResource paging, string? level) { if (!_configFileProvider.LogDbEnabled) { - return new PagingResource(); + return TypedResults.Ok(new PagingResource()); } var pagingResource = new PagingResource(paging); @@ -72,7 +74,7 @@ public PagingResource GetLogs([FromQuery] PagingRequestResource pag response.SortKey = "time"; } - return response; + return TypedResults.Ok(response); } } } diff --git a/src/Sonarr.Api.V5/Logs/LogFileControllerBase.cs b/src/Sonarr.Api.V5/Logs/LogFileControllerBase.cs index 01fed3b7e..877a63653 100644 --- a/src/Sonarr.Api.V5/Logs/LogFileControllerBase.cs +++ b/src/Sonarr.Api.V5/Logs/LogFileControllerBase.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NLog; using NzbDrone.Common.Disk; @@ -24,7 +26,7 @@ public LogFileControllerBase(IDiskProvider diskProvider, [HttpGet] [Produces("application/json")] - public List GetLogFilesResponse() + public Ok> GetLogFilesResponse() { var result = new List(); @@ -45,12 +47,12 @@ public List GetLogFilesResponse() }); } - return result.OrderByDescending(l => l.LastWriteTime).ToList(); + return TypedResults.Ok(result.OrderByDescending(l => l.LastWriteTime).ToList()); } [HttpGet(@"{filename:regex([[-.a-zA-Z0-9]]+?\.txt)}")] [Produces("text/plain")] - public IActionResult GetLogFileResponse(string filename) + public Results GetLogFileResponse(string filename) { LogManager.Flush(); @@ -58,10 +60,10 @@ public IActionResult GetLogFileResponse(string filename) if (!_diskProvider.FileExists(filePath)) { - return NotFound(); + return TypedResults.NotFound(); } - return PhysicalFile(filePath, "text/plain"); + return TypedResults.PhysicalFile(filePath, "text/plain"); } protected abstract IEnumerable GetLogFiles(); diff --git a/src/Sonarr.Api.V5/ManualImport/ManualImportController.cs b/src/Sonarr.Api.V5/ManualImport/ManualImportController.cs index 2d8e8653a..bf9ad9bec 100644 --- a/src/Sonarr.Api.V5/ManualImport/ManualImportController.cs +++ b/src/Sonarr.Api.V5/ManualImport/ManualImportController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Languages; @@ -20,14 +22,14 @@ public ManualImportController(IManualImportService manualImportService) [HttpGet] [Produces("application/json")] - public List GetMediaFiles(string? folder, [FromQuery] string[]? downloadIds, int? seriesId, int? seasonNumber, bool filterExistingFiles = true) + public Ok> GetMediaFiles(string? folder, [FromQuery] string[]? downloadIds, int? seriesId, int? seasonNumber, bool filterExistingFiles = true) { if (seriesId.HasValue && downloadIds == null) { - return _manualImportService.GetMediaFiles(seriesId.Value, seasonNumber) + return TypedResults.Ok(_manualImportService.GetMediaFiles(seriesId.Value, seasonNumber) .ToResource() .Select(AddQualityWeight) - .ToList(); + .ToList()); } if (downloadIds != null && downloadIds.Any()) @@ -39,20 +41,20 @@ public List GetMediaFiles(string? folder, [FromQuery] stri files.AddRange(_manualImportService.GetMediaFiles(null, downloadId, seriesId, filterExistingFiles)); } - return files.ToResource() + return TypedResults.Ok(files.ToResource() .Select(AddQualityWeight) - .ToList(); + .ToList()); } - return _manualImportService.GetMediaFiles(folder, null, seriesId, filterExistingFiles) + return TypedResults.Ok(_manualImportService.GetMediaFiles(folder, null, seriesId, filterExistingFiles) .ToResource() .Select(AddQualityWeight) - .ToList(); + .ToList()); } [HttpPost] [Consumes("application/json")] - public List ReprocessItems([FromBody] List items) + public Results>, BadRequest> ReprocessItems([FromBody] List items) { if (items is { Count: 0 }) { @@ -95,7 +97,7 @@ public List ReprocessItems([FromBody] List UpdateProvider([FromBody] MetadataBulkResource providerResource) + public override Results>, BadRequest> UpdateProvider([FromBody] MetadataBulkResource providerResource) { throw new NotImplementedException(); } [NonAction] - public override ActionResult DeleteProviders([FromBody] MetadataBulkResource resource) + public override NoContent DeleteProviders([FromBody] MetadataBulkResource resource) { throw new NotImplementedException(); } diff --git a/src/Sonarr.Api.V5/Parse/ParseController.cs b/src/Sonarr.Api.V5/Parse/ParseController.cs index d55d5b4d7..8ca7f7b45 100644 --- a/src/Sonarr.Api.V5/Parse/ParseController.cs +++ b/src/Sonarr.Api.V5/Parse/ParseController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.CustomFormats; @@ -28,24 +30,24 @@ public ParseController(IParsingService parsingService, [HttpGet] [Produces("application/json")] - public ParseResource Parse(string? title, string? path) + public Ok Parse(string? title, string? path) { if (title.IsNullOrWhiteSpace()) { - return new ParseResource + return TypedResults.Ok(new ParseResource { Title = title - }; + }); } var parsedEpisodeInfo = path.IsNotNullOrWhiteSpace() ? Parser.ParsePath(path) : Parser.ParseTitle(title); if (parsedEpisodeInfo == null) { - return new ParseResource + return TypedResults.Ok(new ParseResource { Title = title - }; + }); } var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0, 0, null); @@ -57,7 +59,7 @@ public ParseResource Parse(string? title, string? path) remoteEpisode.CustomFormats = _formatCalculator.ParseCustomFormat(remoteEpisode, 0); remoteEpisode.CustomFormatScore = remoteEpisode.Series?.QualityProfile?.Value.CalculateCustomFormatScore(remoteEpisode.CustomFormats) ?? 0; - return new ParseResource + return TypedResults.Ok(new ParseResource { Title = title, ParsedEpisodeInfo = remoteEpisode.ParsedEpisodeInfo, @@ -66,15 +68,15 @@ public ParseResource Parse(string? title, string? path) Languages = remoteEpisode.Languages, CustomFormats = remoteEpisode.CustomFormats?.ToResource(false), CustomFormatScore = remoteEpisode.CustomFormatScore - }; + }); } else { - return new ParseResource + return TypedResults.Ok(new ParseResource { Title = title, ParsedEpisodeInfo = parsedEpisodeInfo - }; + }); } } } diff --git a/src/Sonarr.Api.V5/Profiles/Quality/QualityProfileController.cs b/src/Sonarr.Api.V5/Profiles/Quality/QualityProfileController.cs index a827635ab..4bd8bbc9f 100644 --- a/src/Sonarr.Api.V5/Profiles/Quality/QualityProfileController.cs +++ b/src/Sonarr.Api.V5/Profiles/Quality/QualityProfileController.cs @@ -1,4 +1,6 @@ using FluentValidation; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.CustomFormats; @@ -46,30 +48,30 @@ public QualityProfileController(IQualityProfileService profileService, ICustomFo [RestPostById] [Consumes("application/json")] - public ActionResult Create([FromBody] QualityProfileResource resource) + public Results, NotFound> Create([FromBody] QualityProfileResource resource) { var model = resource.ToModel(); model = _profileService.Add(model); - return Created(model.Id); + return TypedCreated(model.Id); } [RestDeleteById] - public ActionResult DeleteProfile(int id) + public NoContent DeleteProfile(int id) { _profileService.Delete(id); - return NoContent(); + return TypedResults.NoContent(); } [RestPutById] [Consumes("application/json")] - public ActionResult Update([FromBody] QualityProfileResource resource) + public Results, NotFound> Update([FromBody] QualityProfileResource resource) { var model = resource.ToModel(); _profileService.Update(model); - return Accepted(model.Id); + return TypedAccepted(model.Id); } protected override QualityProfileResource GetResourceById(int id) @@ -79,8 +81,8 @@ protected override QualityProfileResource GetResourceById(int id) [HttpGet] [Produces("application/json")] - public List GetAll() + public Ok> GetAll() { - return _profileService.All().ToResource(); + return TypedResults.Ok(_profileService.All().ToResource()); } } diff --git a/src/Sonarr.Api.V5/Profiles/Quality/QualityProfileSchemaController.cs b/src/Sonarr.Api.V5/Profiles/Quality/QualityProfileSchemaController.cs index ba2a0afba..838cef4ec 100644 --- a/src/Sonarr.Api.V5/Profiles/Quality/QualityProfileSchemaController.cs +++ b/src/Sonarr.Api.V5/Profiles/Quality/QualityProfileSchemaController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Profiles.Qualities; using Sonarr.Http; @@ -15,11 +17,11 @@ public QualityProfileSchemaController(IQualityProfileService profileService) } [HttpGet] - public QualityProfileResource GetSchema() + public Ok GetSchema() { var qualityProfile = _profileService.GetDefaultProfile(string.Empty); - return qualityProfile.ToResource(); + return TypedResults.Ok(qualityProfile.ToResource()); } } } diff --git a/src/Sonarr.Api.V5/Profiles/Release/ReleaseProfileController.cs b/src/Sonarr.Api.V5/Profiles/Release/ReleaseProfileController.cs index 2f51a9010..88088a0e5 100644 --- a/src/Sonarr.Api.V5/Profiles/Release/ReleaseProfileController.cs +++ b/src/Sonarr.Api.V5/Profiles/Release/ReleaseProfileController.cs @@ -1,4 +1,6 @@ using FluentValidation; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Indexers; @@ -52,29 +54,29 @@ public ReleaseProfileController(IReleaseProfileService releaseProfileService, II } [RestPostById] - public ActionResult Create([FromBody] ReleaseProfileResource resource) + public Results, NotFound> Create([FromBody] ReleaseProfileResource resource) { var model = resource.ToModel(); model = _releaseProfileService.Add(model); - return Created(model.Id); + return TypedCreated(model.Id); } [RestDeleteById] - public ActionResult DeleteProfile(int id) + public NoContent DeleteProfile(int id) { _releaseProfileService.Delete(id); - return NoContent(); + return TypedResults.NoContent(); } [RestPutById] - public ActionResult Update([FromBody] ReleaseProfileResource resource) + public Results, NotFound> Update([FromBody] ReleaseProfileResource resource) { var model = resource.ToModel(); _releaseProfileService.Update(model); - return Accepted(model.Id); + return TypedAccepted(model.Id); } protected override ReleaseProfileResource GetResourceById(int id) @@ -83,8 +85,8 @@ protected override ReleaseProfileResource GetResourceById(int id) } [HttpGet] - public List GetAll() + public Ok> GetAll() { - return _releaseProfileService.All().ToResource(); + return TypedResults.Ok(_releaseProfileService.All().ToResource()); } } diff --git a/src/Sonarr.Api.V5/Provider/ProviderControllerBase.cs b/src/Sonarr.Api.V5/Provider/ProviderControllerBase.cs index faaa2aa2e..ff95083f9 100644 --- a/src/Sonarr.Api.V5/Provider/ProviderControllerBase.cs +++ b/src/Sonarr.Api.V5/Provider/ProviderControllerBase.cs @@ -1,5 +1,7 @@ using FluentValidation; using FluentValidation.Results; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Common.Serializer; @@ -57,7 +59,7 @@ protected override TProviderResource GetResourceById(int id) [HttpGet] [Produces("application/json")] - public List GetAll() + public Ok> GetAll() { var providerDefinitions = _providerFactory.All(); @@ -70,13 +72,13 @@ public List GetAll() result.Add(_resourceMapper.ToResource(definition)); } - return result.OrderBy(p => p.Name).ToList(); + return TypedResults.Ok(result.OrderBy(p => p.Name).ToList()); } [RestPostById] [Consumes("application/json")] [Produces("application/json")] - public ActionResult CreateProvider([FromBody] TProviderResource providerResource, [FromQuery] bool skipTesting = false, [FromQuery] SkipValidation skipValidation = SkipValidation.None) + public Results, NotFound> CreateProvider([FromBody] TProviderResource providerResource, [FromQuery] bool skipTesting = false, [FromQuery] SkipValidation skipValidation = SkipValidation.None) { var providerDefinition = GetDefinition(providerResource, null, skipValidation, false); @@ -87,20 +89,20 @@ public ActionResult CreateProvider([FromBody] TProviderResour providerDefinition = _providerFactory.Create(providerDefinition); - return Created(providerDefinition.Id); + return TypedCreated(providerDefinition.Id); } [RestPutById] [Consumes("application/json")] [Produces("application/json")] - public ActionResult UpdateProvider([FromRoute] int id, [FromBody] TProviderResource providerResource, [FromQuery] bool skipTesting = false, [FromQuery] SkipValidation skipValidation = SkipValidation.None) + public Results, NotFound> UpdateProvider([FromRoute] int id, [FromBody] TProviderResource providerResource, [FromQuery] bool skipTesting = false, [FromQuery] SkipValidation skipValidation = SkipValidation.None) { // TODO: Remove fallback to Id from body in next API version bump var existingDefinition = _providerFactory.Find(id) ?? _providerFactory.Find(providerResource.Id); if (existingDefinition == null) { - return NotFound(); + return TypedResults.NotFound(); } var providerDefinition = GetDefinition(providerResource, existingDefinition, skipValidation, false); @@ -119,13 +121,13 @@ public ActionResult UpdateProvider([FromRoute] int id, [FromB _providerFactory.Update(providerDefinition); } - return Accepted(existingDefinition.Id); + return TypedAccepted(existingDefinition.Id); } [HttpPut("bulk")] [Consumes("application/json")] [Produces("application/json")] - public virtual ActionResult UpdateProvider([FromBody] TBulkProviderResource providerResource) + public virtual Results>, BadRequest> UpdateProvider([FromBody] TBulkProviderResource providerResource) { if (!providerResource.Ids.Any()) { @@ -160,7 +162,7 @@ public virtual ActionResult UpdateProvider([FromBody] TBulkPr _bulkResourceMapper.UpdateModel(providerResource, definitionsToUpdate); - return Accepted(_providerFactory.Update(definitionsToUpdate).Select(x => _resourceMapper.ToResource(x))); + return TypedResults.Ok(_providerFactory.Update(definitionsToUpdate).Select(x => _resourceMapper.ToResource(x))); } private TProviderDefinition GetDefinition(TProviderResource providerResource, TProviderDefinition? existingDefinition, SkipValidation skipValidation, bool forceValidate) @@ -176,25 +178,25 @@ private TProviderDefinition GetDefinition(TProviderResource providerResource, TP } [RestDeleteById] - public ActionResult DeleteProvider(int id) + public NoContent DeleteProvider(int id) { _providerFactory.Delete(id); - return NoContent(); + return TypedResults.NoContent(); } [HttpDelete("bulk")] [Consumes("application/json")] - public virtual ActionResult DeleteProviders([FromBody] TBulkProviderResource resource) + public virtual NoContent DeleteProviders([FromBody] TBulkProviderResource resource) { _providerFactory.Delete(resource.Ids); - return NoContent(); + return TypedResults.NoContent(); } [HttpGet("schema")] [Produces("application/json")] - public List GetTemplates() + public Ok> GetTemplates() { var defaultDefinitions = _providerFactory.GetDefaultDefinitions().OrderBy(p => p.ImplementationName).ToList(); @@ -212,25 +214,25 @@ public List GetTemplates() result.Add(providerResource); } - return result; + return TypedResults.Ok(result); } [SkipValidation(true, false)] [HttpPost("test")] [Consumes("application/json")] - public ActionResult Test([FromBody] TProviderResource providerResource, [FromQuery] SkipValidation skipValidation = SkipValidation.None) + public NoContent Test([FromBody] TProviderResource providerResource, [FromQuery] SkipValidation skipValidation = SkipValidation.None) { var existingDefinition = providerResource.Id > 0 ? _providerFactory.Find(providerResource.Id) : null; var providerDefinition = GetDefinition(providerResource, existingDefinition, skipValidation, true); Test(providerDefinition, skipValidation); - return NoContent(); + return TypedResults.NoContent(); } [HttpPost("testall")] [Produces("application/json")] - public IActionResult TestAll() + public Results>, BadRequest>> TestAll() { var providerDefinitions = _providerFactory.All() .Where(c => c.Settings.Validate().IsValid && c.Enable) @@ -251,14 +253,14 @@ public IActionResult TestAll() }); } - return result.Any(c => !c.IsValid) ? BadRequest(result) : Ok(result); + return result.Any(c => !c.IsValid) ? TypedResults.BadRequest(result) : TypedResults.Ok(result); } [SkipValidation] [HttpPost("action/{name}")] [Consumes("application/json")] [Produces("application/json")] - public IActionResult RequestAction([FromRoute] string name, [FromBody] TProviderResource providerResource) + public Results RequestAction([FromRoute] string name, [FromBody] TProviderResource providerResource) { var existingDefinition = providerResource.Id > 0 ? _providerFactory.Find(providerResource.Id) : null; var providerDefinition = GetDefinition(providerResource, existingDefinition, SkipValidation.All, false); @@ -267,7 +269,7 @@ public IActionResult RequestAction([FromRoute] string name, [FromBody] TProvider var data = _providerFactory.RequestAction(providerDefinition, name, query); - return Content(data.ToJson(), "application/json"); + return TypedResults.Content(data.ToJson(), "application/json"); } [NonAction] diff --git a/src/Sonarr.Api.V5/Qualities/QualityDefinitionController.cs b/src/Sonarr.Api.V5/Qualities/QualityDefinitionController.cs index ff0bc820f..bf52770f7 100644 --- a/src/Sonarr.Api.V5/Qualities/QualityDefinitionController.cs +++ b/src/Sonarr.Api.V5/Qualities/QualityDefinitionController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Messaging.Events; @@ -29,7 +31,7 @@ public QualityDefinitionController( } [RestPutById] - public ActionResult Update([FromBody] QualityDefinitionResource resource) + public Results, NotFound> Update([FromBody] QualityDefinitionResource resource) { var model = resource.ToModel(); _qualityDefinitionService.Update(model); @@ -39,7 +41,7 @@ public ActionResult Update([FromBody] QualityDefiniti _qualityProfileService.UpdateAllSizeLimits(new QualityProfileSizeLimit(model)); } - return Accepted(model.Id); + return TypedAccepted(model.Id); } protected override QualityDefinitionResource GetResourceById(int id) @@ -48,13 +50,13 @@ protected override QualityDefinitionResource GetResourceById(int id) } [HttpGet] - public List GetAll() + public Ok> GetAll() { - return _qualityDefinitionService.All().ToResource(); + return TypedResults.Ok(_qualityDefinitionService.All().ToResource()); } [HttpPut] - public object UpdateMany([FromBody] List resource) + public Ok> UpdateMany([FromBody] List resource) { // Read from request var qualityDefinitions = resource.ToModel().ToList(); @@ -71,8 +73,7 @@ public object UpdateMany([FromBody] List resource) _qualityProfileService.UpdateAllSizeLimits(toUpdate); } - return Accepted(_qualityDefinitionService.All() - .ToResource()); + return TypedResults.Ok(_qualityDefinitionService.All().ToResource()); } [NonAction] diff --git a/src/Sonarr.Api.V5/Queue/QueueActionController.cs b/src/Sonarr.Api.V5/Queue/QueueActionController.cs index c9e79e5ee..3b4c97d86 100644 --- a/src/Sonarr.Api.V5/Queue/QueueActionController.cs +++ b/src/Sonarr.Api.V5/Queue/QueueActionController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Download; using NzbDrone.Core.Download.Pending; @@ -20,7 +22,7 @@ public QueueActionController(IPendingReleaseService pendingReleaseService, } [HttpPost("grab/{id:int}")] - public async Task Grab([FromRoute] int id) + public async Task Grab([FromRoute] int id) { var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id); @@ -31,12 +33,12 @@ public async Task Grab([FromRoute] int id) await _downloadService.DownloadReport(pendingRelease.RemoteEpisode, null); - return NoContent(); + return TypedResults.NoContent(); } [HttpPost("grab/bulk")] [Consumes("application/json")] - public async Task Grab([FromBody] QueueBulkResource resource) + public async Task Grab([FromBody] QueueBulkResource resource) { foreach (var id in resource.Ids) { @@ -50,7 +52,7 @@ public async Task Grab([FromBody] QueueBulkResource resource) await _downloadService.DownloadReport(pendingRelease.RemoteEpisode, null); } - return NoContent(); + return TypedResults.NoContent(); } } } diff --git a/src/Sonarr.Api.V5/Queue/QueueController.cs b/src/Sonarr.Api.V5/Queue/QueueController.cs index 4fe384345..3ad9418be 100644 --- a/src/Sonarr.Api.V5/Queue/QueueController.cs +++ b/src/Sonarr.Api.V5/Queue/QueueController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Blocklisting; @@ -57,7 +59,7 @@ public QueueController(IBroadcastSignalRMessage broadcastSignalRMessage, } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } @@ -68,7 +70,7 @@ protected override QueueResource GetResourceById(int id) } [RestDeleteById] - public ActionResult RemoveAction(int id, string? message = null, bool removeFromClient = true, bool blocklist = false, bool skipRedownload = false, bool changeCategory = false) + public Results RemoveAction(int id, string? message = null, bool removeFromClient = true, bool blocklist = false, bool skipRedownload = false, bool changeCategory = false) { var pendingRelease = _pendingReleaseService.FindPendingQueueItem(id); @@ -76,7 +78,7 @@ public ActionResult RemoveAction(int id, string? message = null, bool removeFrom { Remove(pendingRelease, message, blocklist); - return NoContent(); + return TypedResults.NoContent(); } var trackedDownload = GetTrackedDownload(id); @@ -89,11 +91,11 @@ public ActionResult RemoveAction(int id, string? message = null, bool removeFrom Remove(trackedDownload, message, removeFromClient, blocklist, skipRedownload, changeCategory); _trackedDownloadService.StopTracking(trackedDownload.DownloadItem.DownloadId); - return NoContent(); + return TypedResults.NoContent(); } [HttpDelete("bulk")] - public object RemoveMany([FromBody] QueueBulkResource resource, [FromQuery] string? message, [FromQuery] bool removeFromClient = true, [FromQuery] bool blocklist = false, [FromQuery] bool skipRedownload = false, [FromQuery] bool changeCategory = false) + public NoContent RemoveMany([FromBody] QueueBulkResource resource, [FromQuery] string? message, [FromQuery] bool removeFromClient = true, [FromQuery] bool blocklist = false, [FromQuery] bool skipRedownload = false, [FromQuery] bool changeCategory = false) { var trackedDownloadIds = new List(); var pendingToRemove = new List(); @@ -130,12 +132,12 @@ public object RemoveMany([FromBody] QueueBulkResource resource, [FromQuery] stri _trackedDownloadService.StopTracking(trackedDownloadIds); - return NoContent(); + return TypedResults.NoContent(); } [HttpGet] [Produces("application/json")] - public PagingResource GetQueue([FromQuery] PagingRequestResource paging, bool includeUnknownSeriesItems = true, [FromQuery] int[]? seriesIds = null, DownloadProtocol? protocol = null, [FromQuery] int[]? languages = null, [FromQuery] int[]? quality = null, [FromQuery] QueueStatus[]? status = null, [FromQuery] QueueSubresource[]? includeSubresources = null) + public Ok> GetQueue([FromQuery] PagingRequestResource paging, bool includeUnknownSeriesItems = true, [FromQuery] int[]? seriesIds = null, DownloadProtocol? protocol = null, [FromQuery] int[]? languages = null, [FromQuery] int[]? quality = null, [FromQuery] QueueStatus[]? status = null, [FromQuery] QueueSubresource[]? includeSubresources = null) { var pagingResource = new PagingResource(paging); var pagingSpec = pagingResource.MapToPagingSpec( @@ -167,7 +169,7 @@ public PagingResource GetQueue([FromQuery] PagingRequestResource var includeSeries = includeSubresources.Contains(QueueSubresource.Series); var includeEpisodes = includeSubresources.Contains(QueueSubresource.Episodes); - return pagingSpec.ApplyToPage((spec) => GetQueue(spec, seriesIds?.ToHashSet() ?? [], protocol, languages?.ToHashSet() ?? [], quality?.ToHashSet() ?? [], status?.ToHashSet() ?? [], includeUnknownSeriesItems), (q) => MapToResource(q, includeSeries, includeEpisodes)); + return TypedResults.Ok(pagingSpec.ApplyToPage((spec) => GetQueue(spec, seriesIds?.ToHashSet() ?? [], protocol, languages?.ToHashSet() ?? [], quality?.ToHashSet() ?? [], status?.ToHashSet() ?? [], includeUnknownSeriesItems), (q) => MapToResource(q, includeSeries, includeEpisodes))); } private PagingSpec GetQueue(PagingSpec pagingSpec, HashSet seriesIds, DownloadProtocol? protocol, HashSet languages, HashSet quality, HashSet status, bool includeUnknownSeriesItems) diff --git a/src/Sonarr.Api.V5/Queue/QueueDetailsController.cs b/src/Sonarr.Api.V5/Queue/QueueDetailsController.cs index c6d5526a9..c4ebdd73d 100644 --- a/src/Sonarr.Api.V5/Queue/QueueDetailsController.cs +++ b/src/Sonarr.Api.V5/Queue/QueueDetailsController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Datastore.Events; @@ -25,7 +27,7 @@ public QueueDetailsController(IBroadcastSignalRMessage broadcastSignalRMessage, } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } @@ -37,7 +39,7 @@ protected override QueueResource GetResourceById(int id) [HttpGet] [Produces("application/json")] - public List GetQueue(int? seriesId, [FromQuery]List episodeIds, [FromQuery] QueueSubresource[]? includeSubresources = null) + public Ok> GetQueue(int? seriesId, [FromQuery]List episodeIds, [FromQuery] QueueSubresource[]? includeSubresources = null) { var queue = _queueService.GetQueue(); var pending = _pendingReleaseService.GetPendingQueue(); @@ -47,17 +49,17 @@ public List GetQueue(int? seriesId, [FromQuery]List episodeI if (seriesId.HasValue) { - return fullQueue.Where(q => q.Series?.Id == seriesId).ToResource(includeSeries, includeEpisodes); + return TypedResults.Ok(fullQueue.Where(q => q.Series?.Id == seriesId).ToResource(includeSeries, includeEpisodes)); } if (episodeIds.Any()) { - return fullQueue.Where(q => q.Episodes.Any() && + return TypedResults.Ok(fullQueue.Where(q => q.Episodes.Any() && episodeIds.IntersectBy(e => e, q.Episodes, e => e.Id, null).Any()) - .ToResource(includeSeries, includeEpisodes); + .ToResource(includeSeries, includeEpisodes)); } - return fullQueue.ToResource(includeSeries, includeEpisodes); + return TypedResults.Ok(fullQueue.ToResource(includeSeries, includeEpisodes)); } [NonAction] diff --git a/src/Sonarr.Api.V5/Queue/QueueStatusController.cs b/src/Sonarr.Api.V5/Queue/QueueStatusController.cs index ff57c76bb..7ba949bbe 100644 --- a/src/Sonarr.Api.V5/Queue/QueueStatusController.cs +++ b/src/Sonarr.Api.V5/Queue/QueueStatusController.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Download.Pending; @@ -29,7 +30,7 @@ public QueueStatusController(IBroadcastSignalRMessage broadcastSignalRMessage, I } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } diff --git a/src/Sonarr.Api.V5/Release/ReleaseController.cs b/src/Sonarr.Api.V5/Release/ReleaseController.cs index 66a6b1ac5..968684419 100644 --- a/src/Sonarr.Api.V5/Release/ReleaseController.cs +++ b/src/Sonarr.Api.V5/Release/ReleaseController.cs @@ -1,4 +1,6 @@ using FluentValidation; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NLog; using NzbDrone.Common.Cache; @@ -71,7 +73,7 @@ public ReleaseController(IFetchAndParseRss rssFetcherAndParser, } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } @@ -83,7 +85,7 @@ protected override ReleaseResource GetResourceById(int id) [HttpPost] [Consumes("application/json")] - public async Task DownloadRelease([FromBody] ReleaseGrabResource release) + public async Task, NotFound>> DownloadRelease([FromBody] ReleaseGrabResource release) { var remoteEpisode = _remoteEpisodeCache.Find(GetCacheKey(release)); @@ -182,24 +184,24 @@ public async Task DownloadRelease([FromBody] ReleaseGrabResource release throw new NzbDroneClientException(HttpStatusCode.Conflict, "Getting release from indexer failed"); } - return release; + return TypedResults.Ok(release); } [HttpGet] [Produces("application/json")] - public async Task> GetReleases(int? seriesId, int? episodeId, int? seasonNumber) + public async Task>, BadRequest>> GetReleases(int? seriesId, int? episodeId, int? seasonNumber) { if (episodeId.HasValue) { - return await GetEpisodeReleases(episodeId.Value); + return TypedResults.Ok(await GetEpisodeReleases(episodeId.Value)); } if (seriesId.HasValue && seasonNumber.HasValue) { - return await GetSeasonReleases(seriesId.Value, seasonNumber.Value); + return TypedResults.Ok(await GetSeasonReleases(seriesId.Value, seasonNumber.Value)); } - return await GetRss(); + return TypedResults.Ok(await GetRss()); } private async Task> GetEpisodeReleases(int episodeId) diff --git a/src/Sonarr.Api.V5/Release/ReleasePushController.cs b/src/Sonarr.Api.V5/Release/ReleasePushController.cs index a23e99422..8363f2d92 100644 --- a/src/Sonarr.Api.V5/Release/ReleasePushController.cs +++ b/src/Sonarr.Api.V5/Release/ReleasePushController.cs @@ -1,5 +1,7 @@ using FluentValidation; using FluentValidation.Results; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NLog; using NzbDrone.Common.Extensions; @@ -50,7 +52,7 @@ public ReleasePushController(IMakeDownloadDecision downloadDecisionMaker, [HttpPost] [Consumes("application/json")] - public ActionResult Create([FromBody] ReleasePushResource release) + public Results, BadRequest> Create([FromBody] ReleasePushResource release) { _logger.Info("Release pushed: {0} - {1}", release.Title, release.DownloadUrl ?? release.MagnetUrl); @@ -80,7 +82,7 @@ public ActionResult Create([FromBody] ReleasePushResource relea throw new ValidationException(new List { new("Title", "Unable to parse", release.Title) }); } - return decision.MapDecision(1, _qualityProfile); + return TypedResults.Ok(decision.MapDecision(1, _qualityProfile)); } private void ResolveIndexer(ReleaseInfo release) diff --git a/src/Sonarr.Api.V5/RemotePathMappings/RemotePathMappingController.cs b/src/Sonarr.Api.V5/RemotePathMappings/RemotePathMappingController.cs index ca7defaf1..36f167e61 100644 --- a/src/Sonarr.Api.V5/RemotePathMappings/RemotePathMappingController.cs +++ b/src/Sonarr.Api.V5/RemotePathMappings/RemotePathMappingController.cs @@ -1,4 +1,6 @@ using FluentValidation; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.RemotePathMappings; using NzbDrone.Core.Validation.Paths; @@ -47,33 +49,33 @@ protected override RemotePathMappingResource GetResourceById(int id) [RestPostById] [Consumes("application/json")] - public ActionResult CreateMapping([FromBody] RemotePathMappingResource resource) + public Results, NotFound> CreateMapping([FromBody] RemotePathMappingResource resource) { var model = resource.ToModel(); - return Created(_remotePathMappingService.Add(model).Id); + return TypedCreated(_remotePathMappingService.Add(model).Id); } [HttpGet] [Produces("application/json")] - public List GetMappings() + public Ok> GetMappings() { - return _remotePathMappingService.All().ToResource(); + return TypedResults.Ok(_remotePathMappingService.All().ToResource()); } [RestDeleteById] - public ActionResult DeleteMapping(int id) + public NoContent DeleteMapping(int id) { _remotePathMappingService.Remove(id); - return NoContent(); + return TypedResults.NoContent(); } [RestPutById] - public ActionResult UpdateMapping([FromBody] RemotePathMappingResource resource) + public Results, NotFound> UpdateMapping([FromBody] RemotePathMappingResource resource) { var mapping = resource.ToModel(); - return Accepted(_remotePathMappingService.Update(mapping)); + return TypedResults.Ok(_remotePathMappingService.Update(mapping).ToResource()); } } diff --git a/src/Sonarr.Api.V5/RootFolders/RootFolderController.cs b/src/Sonarr.Api.V5/RootFolders/RootFolderController.cs index 19b9a3aec..3f650a693 100644 --- a/src/Sonarr.Api.V5/RootFolders/RootFolderController.cs +++ b/src/Sonarr.Api.V5/RootFolders/RootFolderController.cs @@ -1,4 +1,6 @@ using FluentValidation; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.RootFolders; using NzbDrone.Core.Validation.Paths; @@ -49,25 +51,25 @@ protected override RootFolderResource GetResourceById(int id) [RestPostById] [Consumes("application/json")] - public ActionResult CreateRootFolder([FromBody] RootFolderResource rootFolderResource) + public Results, NotFound> CreateRootFolder([FromBody] RootFolderResource rootFolderResource) { var model = rootFolderResource.ToModel(); - return Created(_rootFolderService.Add(model).Id); + return TypedCreated(_rootFolderService.Add(model).Id); } [HttpGet] [Produces("application/json")] - public List GetRootFolders() + public Ok> GetRootFolders() { - return _rootFolderService.AllWithUnmappedFolders().ToResource(); + return TypedResults.Ok(_rootFolderService.AllWithUnmappedFolders().ToResource()); } [RestDeleteById] - public ActionResult DeleteFolder(int id) + public NoContent DeleteFolder(int id) { _rootFolderService.Remove(id); - return NoContent(); + return TypedResults.NoContent(); } } diff --git a/src/Sonarr.Api.V5/SeasonPass/SeasonPassController.cs b/src/Sonarr.Api.V5/SeasonPass/SeasonPassController.cs index dc55e7d74..28ddcd5c7 100644 --- a/src/Sonarr.Api.V5/SeasonPass/SeasonPassController.cs +++ b/src/Sonarr.Api.V5/SeasonPass/SeasonPassController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Tv; using Sonarr.Http; @@ -18,7 +20,7 @@ public SeasonPassController(ISeriesService seriesService, IEpisodeMonitoredServi [HttpPost] [Consumes("application/json")] - public IActionResult UpdateAll([FromBody] SeasonPassResource resource) + public NoContent UpdateAll([FromBody] SeasonPassResource resource) { var seriesToUpdate = _seriesService.GetSeries(resource.Series.Select(s => s.Id)); @@ -52,6 +54,6 @@ public IActionResult UpdateAll([FromBody] SeasonPassResource resource) _episodeMonitoredService.SetEpisodeMonitoredStatus(series, resource.MonitoringOptions.ToModel()); } - return NoContent(); + return TypedResults.NoContent(); } } diff --git a/src/Sonarr.Api.V5/Series/SeriesController.cs b/src/Sonarr.Api.V5/Series/SeriesController.cs index 79139b1f5..a0c9ffafc 100644 --- a/src/Sonarr.Api.V5/Series/SeriesController.cs +++ b/src/Sonarr.Api.V5/Series/SeriesController.cs @@ -1,4 +1,6 @@ using FluentValidation; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Common.TPL; @@ -107,7 +109,7 @@ public SeriesController(IBroadcastSignalRMessage signalRBroadcaster, [HttpGet] [Produces("application/json")] - public List AllSeries(int? tvdbId, [FromQuery] SeriesSubresource[]? includeSubresources = null) + public Ok> AllSeries(int? tvdbId, [FromQuery] SeriesSubresource[]? includeSubresources = null) { var seriesStats = _seriesStatisticsService.SeriesStatistics(); var seriesResources = new List(); @@ -127,18 +129,18 @@ public List AllSeries(int? tvdbId, [FromQuery] SeriesSubresource PopulateAlternateTitles(seriesResources); seriesResources.ForEach(LinkRootFolderPath); - return seriesResources; + return TypedResults.Ok(seriesResources); } [NonAction] - public override ActionResult GetResourceByIdWithErrorHandler(int id) + public override Results, NotFound> GetResourceByIdWithErrorHandler(int id) { return base.GetResourceByIdWithErrorHandler(id); } [RestGetById] [Produces("application/json")] - public ActionResult GetResourceByIdWithErrorHandler(int id, [FromQuery] SeriesSubresource[]? includeSubresources = null) + public Results, NotFound> GetResourceByIdWithErrorHandler(int id, [FromQuery] SeriesSubresource[]? includeSubresources = null) { var includeSeasonImages = includeSubresources.Contains(SeriesSubresource.SeasonImages); @@ -146,11 +148,11 @@ public ActionResult GetResourceByIdWithErrorHandler(int id, [Fro { var series = GetSeriesResourceById(id, includeSeasonImages); - return series == null ? NotFound() : series; + return series == null ? TypedResults.NotFound() : TypedResults.Ok(series); } catch (ModelNotFoundException) { - return NotFound(); + return TypedResults.NotFound(); } } @@ -181,17 +183,17 @@ public ActionResult GetResourceByIdWithErrorHandler(int id, [Fro [RestPostById] [Consumes("application/json")] [Produces("application/json")] - public ActionResult AddSeries([FromBody] SeriesResource seriesResource) + public Results, NotFound> AddSeries([FromBody] SeriesResource seriesResource) { var series = _addSeriesService.AddSeries(seriesResource.ToModel()); - return Created(series.Id); + return TypedCreated(series.Id); } [RestPutById] [Consumes("application/json")] [Produces("application/json")] - public ActionResult UpdateSeries([FromBody] SeriesResource seriesResource, [FromQuery] bool moveFiles = false) + public Results, NotFound> UpdateSeries([FromBody] SeriesResource seriesResource, [FromQuery] bool moveFiles = false) { var series = _seriesService.GetSeries(seriesResource.Id); @@ -215,13 +217,13 @@ public ActionResult UpdateSeries([FromBody] SeriesResource serie BroadcastResourceChange(ModelAction.Updated, seriesResource); - return Accepted(seriesResource.Id); + return TypedAccepted(seriesResource.Id); } [HttpPut("{id}/season")] [Consumes("application/json")] [Produces("application/json")] - public ActionResult UpdateSeasonMonitored([FromRoute] int id, [FromBody] SeasonResource seasonResource) + public Results, NotFound> UpdateSeasonMonitored([FromRoute] int id, [FromBody] SeasonResource seasonResource) { lock (_seriesLockPool.GetLock(id)) { @@ -230,7 +232,7 @@ public ActionResult UpdateSeasonMonitored([FromRoute] int id, [F if (season == null) { - return NotFound(); + return TypedResults.NotFound(); } season.Monitored = seasonResource.Monitored; @@ -239,16 +241,16 @@ public ActionResult UpdateSeasonMonitored([FromRoute] int id, [F BroadcastResourceChange(ModelAction.Updated, series.ToResource()); - return season.ToResource(); + return TypedResults.Ok(season.ToResource()); } } [RestDeleteById] - public ActionResult DeleteSeries(int id, bool deleteFiles = false, bool addImportListExclusion = false) + public NoContent DeleteSeries(int id, bool deleteFiles = false, bool addImportListExclusion = false) { _seriesService.DeleteSeries(new List { id }, deleteFiles, addImportListExclusion); - return NoContent(); + return TypedResults.NoContent(); } private SeriesResource? GetSeriesResource(NzbDrone.Core.Tv.Series? series, bool includeSeasonImages) diff --git a/src/Sonarr.Api.V5/Series/SeriesEditorController.cs b/src/Sonarr.Api.V5/Series/SeriesEditorController.cs index ae30ff4c3..dd3430b4b 100644 --- a/src/Sonarr.Api.V5/Series/SeriesEditorController.cs +++ b/src/Sonarr.Api.V5/Series/SeriesEditorController.cs @@ -1,4 +1,6 @@ using FluentValidation; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Messaging.Commands; @@ -23,7 +25,7 @@ public SeriesEditorController(ISeriesService seriesService, IManageCommandQueue } [HttpPut] - public object SaveAll([FromBody] SeriesEditorResource resource) + public Results>, BadRequest> SaveAll([FromBody] SeriesEditorResource resource) { var seriesToUpdate = _seriesService.GetSeries(resource.SeriesIds); var seriesToMove = new List(); @@ -101,14 +103,14 @@ public object SaveAll([FromBody] SeriesEditorResource resource) }); } - return Accepted(_seriesService.UpdateSeries(seriesToUpdate, !resource.MoveFiles).ToResource()); + return TypedResults.Ok(_seriesService.UpdateSeries(seriesToUpdate, !resource.MoveFiles).ToResource()); } [HttpDelete] - public object DeleteSeries([FromBody] SeriesEditorResource resource) + public NoContent DeleteSeries([FromBody] SeriesEditorResource resource) { _seriesService.DeleteSeries(resource.SeriesIds, resource.DeleteFiles, resource.AddImportListExclusion); - return NoContent(); + return TypedResults.NoContent(); } } diff --git a/src/Sonarr.Api.V5/Series/SeriesFolderController.cs b/src/Sonarr.Api.V5/Series/SeriesFolderController.cs index b26d71286..fd1445d3b 100644 --- a/src/Sonarr.Api.V5/Series/SeriesFolderController.cs +++ b/src/Sonarr.Api.V5/Series/SeriesFolderController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Organizer; using NzbDrone.Core.Tv; @@ -19,14 +21,14 @@ public SeriesFolderController(ISeriesService seriesService, IBuildFileNames file [HttpGet("{id}/folder")] [Produces("application/json")] - public object GetFolder([FromRoute] int id) + public Ok GetFolder([FromRoute] int id) { var series = _seriesService.GetSeries(id); var folder = _fileNameBuilder.GetSeriesFolder(series); - return new + return TypedResults.Ok((object)new { folder - }; + }); } } diff --git a/src/Sonarr.Api.V5/Series/SeriesImportController.cs b/src/Sonarr.Api.V5/Series/SeriesImportController.cs index dd8d6b62a..dd5543290 100644 --- a/src/Sonarr.Api.V5/Series/SeriesImportController.cs +++ b/src/Sonarr.Api.V5/Series/SeriesImportController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Tv; using Sonarr.Http; @@ -15,11 +17,11 @@ public SeriesImportController(IAddSeriesService addSeriesService) } [HttpPost] - public object Import([FromBody] List resource) + public Ok> Import([FromBody] List resource) { var newSeries = resource.ToModel(); - return _addSeriesService.AddSeries(newSeries).ToResource(); + return TypedResults.Ok(_addSeriesService.AddSeries(newSeries).ToResource()); } } } diff --git a/src/Sonarr.Api.V5/Series/SeriesLookupController.cs b/src/Sonarr.Api.V5/Series/SeriesLookupController.cs index 776fc0484..f5a697109 100644 --- a/src/Sonarr.Api.V5/Series/SeriesLookupController.cs +++ b/src/Sonarr.Api.V5/Series/SeriesLookupController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.ImportLists.Exclusions; using NzbDrone.Core.MediaCover; @@ -25,10 +27,10 @@ public SeriesLookupController(ISearchForNewSeries searchProxy, IBuildFileNames f } [HttpGet] - public IEnumerable Search([FromQuery] string term) + public Ok> Search([FromQuery] string term) { var tvDbResults = _searchProxy.SearchForNewSeries(term); - return MapToResource(tvDbResults); + return TypedResults.Ok(MapToResource(tvDbResults)); } private IEnumerable MapToResource(IEnumerable series) diff --git a/src/Sonarr.Api.V5/Settings/GeneralSettingsController.cs b/src/Sonarr.Api.V5/Settings/GeneralSettingsController.cs index 7b4ba433a..83e27b9b5 100644 --- a/src/Sonarr.Api.V5/Settings/GeneralSettingsController.cs +++ b/src/Sonarr.Api.V5/Settings/GeneralSettingsController.cs @@ -1,5 +1,5 @@ using FluentValidation; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Http.HttpResults; using NzbDrone.Common.Disk; using NzbDrone.Common.Extensions; using NzbDrone.Core.Authentication; @@ -102,7 +102,7 @@ protected override GeneralSettingsResource ToResource(IConfigFileProvider config return resource; } - public override ActionResult SaveSettings(GeneralSettingsResource resource) + public override Results, NotFound> SaveSettings(GeneralSettingsResource resource) { if (resource.Username.IsNotNullOrWhiteSpace() && resource.Password.IsNotNullOrWhiteSpace()) { diff --git a/src/Sonarr.Api.V5/Settings/NamingSettingsController.cs b/src/Sonarr.Api.V5/Settings/NamingSettingsController.cs index 5c0210fb6..69d4fa48b 100644 --- a/src/Sonarr.Api.V5/Settings/NamingSettingsController.cs +++ b/src/Sonarr.Api.V5/Settings/NamingSettingsController.cs @@ -1,5 +1,7 @@ using FluentValidation; using FluentValidation.Results; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Organizer; @@ -36,27 +38,24 @@ public NamingSettingsController(INamingConfigService namingConfigService, protected override NamingSettingsResource GetResourceById(int id) { - return GetNamingConfig(); + return _namingConfigService.GetConfig().ToResource(); } [HttpGet] - public NamingSettingsResource GetNamingConfig() + public Ok GetNamingConfig() { - var nameSpec = _namingConfigService.GetConfig(); - var resource = nameSpec.ToResource(); - - return resource; + return TypedResults.Ok(GetResourceById(1)); } [RestPutById] - public ActionResult UpdateNamingConfig([FromBody] NamingSettingsResource resource) + public Results, NotFound> UpdateNamingConfig([FromBody] NamingSettingsResource resource) { var nameSpec = resource.ToModel(); ValidateFormatResult(nameSpec); _namingConfigService.Save(nameSpec); - return Accepted(resource.Id); + return TypedAccepted(resource.Id); } [HttpGet("examples")] @@ -64,7 +63,7 @@ public object GetExamples([FromQuery]NamingSettingsResource settings) { if (settings.Id == 0) { - settings = GetNamingConfig(); + settings = GetResourceById(1); } var nameSpec = settings.ToModel(); diff --git a/src/Sonarr.Api.V5/Settings/SettingsController.cs b/src/Sonarr.Api.V5/Settings/SettingsController.cs index cc515a6f4..c37e2052c 100644 --- a/src/Sonarr.Api.V5/Settings/SettingsController.cs +++ b/src/Sonarr.Api.V5/Settings/SettingsController.cs @@ -1,4 +1,6 @@ using System.Reflection; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Configuration; using Sonarr.Http.REST; @@ -19,23 +21,24 @@ protected SettingsController(IConfigFileProvider configFileProvider, IConfigServ } protected override TResource GetResourceById(int id) - { - return GetConfig(); - } - - [HttpGet] - [Produces("application/json")] - public TResource GetConfig() { var resource = ToResource(_configFileProvider, _configService); - resource.Id = 1; + resource.Id = id; return resource; } + [HttpGet] + [Produces("application/json")] + public Ok GetConfig() + { + return TypedResults.Ok(GetResourceById(1)); + } + [RestPutById] [Consumes("application/json")] - public virtual ActionResult SaveSettings([FromBody] TResource resource) + [Produces("application/json")] + public virtual Results, NotFound> SaveSettings([FromBody] TResource resource) { var dictionary = resource.GetType() .GetProperties(BindingFlags.Instance | BindingFlags.Public) @@ -44,7 +47,7 @@ public virtual ActionResult SaveSettings([FromBody] TResource resourc _configFileProvider.SaveConfigDictionary(dictionary); _configService.SaveConfigDictionary(dictionary); - return Accepted(resource.Id); + return TypedAccepted(resource.Id); } protected abstract TResource ToResource(IConfigFileProvider configFile, IConfigService model); diff --git a/src/Sonarr.Api.V5/System/Backup/BackupController.cs b/src/Sonarr.Api.V5/System/Backup/BackupController.cs index 483d539c8..6c729d50d 100644 --- a/src/Sonarr.Api.V5/System/Backup/BackupController.cs +++ b/src/Sonarr.Api.V5/System/Backup/BackupController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Crypto; using NzbDrone.Common.Disk; @@ -29,7 +31,7 @@ public BackupController(IBackupService backupService, [HttpGet] [Produces("application/json")] - public ActionResult> GetAll() + public Ok> GetAll() { var backups = _backupService.GetBackups(); @@ -45,11 +47,11 @@ public ActionResult> GetAll() .OrderByDescending(b => b.Time) .ToList(); - return resources; + return TypedResults.Ok(resources); } [RestDeleteById] - public ActionResult Delete(int id) + public Results Delete(int id) { var backup = GetBackupById(id); @@ -67,30 +69,30 @@ public ActionResult Delete(int id) _diskProvider.DeleteFile(path); - return NoContent(); + return TypedResults.NoContent(); } [HttpPost("restore/{id:int}")] [Produces("application/json")] - public ActionResult Restore([FromRoute] int id) + public Results, NotFound> Restore([FromRoute] int id) { var backup = GetBackupById(id); if (backup == null) { - return NotFound(); + return TypedResults.NotFound(); } var path = GetBackupPath(backup); _backupService.Restore(path); - return new { RestartRequired = true }; + return TypedResults.Ok((object)new { RestartRequired = true }); } [HttpPost("restore/upload")] [Produces("application/json")] [RequestFormLimits(MultipartBodyLengthLimit = 5000000000)] - public ActionResult RestoreUpload() + public Results, BadRequest> RestoreUpload() { var files = Request.Form.Files; @@ -104,7 +106,7 @@ public ActionResult RestoreUpload() if (!ValidExtensions.Contains(extension)) { - return BadRequest(new { error = $"Invalid extension, must be one of: {string.Join(", ", ValidExtensions)}" }); + return TypedResults.BadRequest((object)new { error = $"Invalid extension, must be one of: {string.Join(", ", ValidExtensions)}" }); } var path = Path.Combine(_appFolderInfo.TempFolder, $"sonarr_backup_restore{extension}"); @@ -113,7 +115,7 @@ public ActionResult RestoreUpload() _backupService.Restore(path); _diskProvider.DeleteFile(path); - return new { RestartRequired = true }; + return TypedResults.Ok((object)new { RestartRequired = true }); } private string GetBackupPath(NzbDrone.Core.Backup.Backup backup) diff --git a/src/Sonarr.Api.V5/System/SystemController.cs b/src/Sonarr.Api.V5/System/SystemController.cs index 26e525f19..1a1ffd44c 100644 --- a/src/Sonarr.Api.V5/System/SystemController.cs +++ b/src/Sonarr.Api.V5/System/SystemController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Internal; @@ -53,9 +55,9 @@ public SystemController(IAppFolderInfo appFolderInfo, [HttpGet("status")] [Produces("application/json")] - public SystemResource GetStatus() + public Ok GetStatus() { - return new SystemResource + return TypedResults.Ok(new SystemResource { AppName = BuildInfo.AppName, InstanceName = _configFileProvider.InstanceName, @@ -88,39 +90,39 @@ public SystemResource GetStatus() PackageAuthor = _deploymentInfoProvider.PackageAuthor, PackageUpdateMechanism = _deploymentInfoProvider.PackageUpdateMechanism, PackageUpdateMechanismMessage = _deploymentInfoProvider.PackageUpdateMechanismMessage - }; + }); } [HttpGet("routes")] [Produces("application/json")] - public IActionResult GetRoutes() + public ContentHttpResult GetRoutes() { using (var sw = new StringWriter()) { _graphWriter.Write(_endpointData, sw); var graph = sw.ToString(); - return Content(graph, "text/plain"); + return TypedResults.Content(graph, "text/plain"); } } [HttpGet("routes/duplicate")] [Produces("application/json")] - public object DuplicateRoutes() + public Ok>> DuplicateRoutes() { - return _detector.GetDuplicateEndpoints(_endpointData); + return TypedResults.Ok(_detector.GetDuplicateEndpoints(_endpointData)); } [HttpPost("shutdown")] - public object Shutdown() + public Ok Shutdown() { Task.Factory.StartNew(() => _lifecycleService.Shutdown()); - return new { ShuttingDown = true }; + return TypedResults.Ok((object)new { ShuttingDown = true }); } [HttpPost("restart")] - public object Restart() + public Ok Restart() { Task.Factory.StartNew(() => _lifecycleService.Restart()); - return new { Restarting = true }; + return TypedResults.Ok((object)new { Restarting = true }); } } diff --git a/src/Sonarr.Api.V5/System/Tasks/TaskController.cs b/src/Sonarr.Api.V5/System/Tasks/TaskController.cs index 77b103632..912b87603 100644 --- a/src/Sonarr.Api.V5/System/Tasks/TaskController.cs +++ b/src/Sonarr.Api.V5/System/Tasks/TaskController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.Extensions; using NzbDrone.Core.Datastore.Events; @@ -22,12 +24,12 @@ public TaskController(ITaskManager taskManager, IBroadcastSignalRMessage broadca [HttpGet] [Produces("application/json")] - public ActionResult> GetAll() + public Ok> GetAll() { - return _taskManager.GetAll() + return TypedResults.Ok(_taskManager.GetAll() .Select(ConvertToResource) .OrderBy(t => t.Name) - .ToList(); + .ToList()); } protected override TaskResource? GetResourceById(int id) diff --git a/src/Sonarr.Api.V5/Tags/TagController.cs b/src/Sonarr.Api.V5/Tags/TagController.cs index 395d2847f..c39db3dc3 100644 --- a/src/Sonarr.Api.V5/Tags/TagController.cs +++ b/src/Sonarr.Api.V5/Tags/TagController.cs @@ -1,5 +1,7 @@ using System.Text.RegularExpressions; using FluentValidation; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.AutoTagging; using NzbDrone.Core.Datastore.Events; @@ -38,30 +40,32 @@ protected override TagResource GetResourceById(int id) [HttpGet] [Produces("application/json")] - public List GetAll() + public Ok> GetAll() { - return _tagService.All().ToResource(); + return TypedResults.Ok(_tagService.All().ToResource()); } [RestPostById] [Consumes("application/json")] - public ActionResult Create([FromBody] TagResource resource) + public Results, NotFound> Create([FromBody] TagResource resource) { - return Created(_tagService.Add(resource.ToModel()).Id); + return TypedCreated(_tagService.Add(resource.ToModel()).Id); } [RestPutById] [Consumes("application/json")] - public ActionResult Update([FromBody] TagResource resource) + public Results, NotFound> Update([FromBody] TagResource resource) { _tagService.Update(resource.ToModel()); - return Accepted(resource.Id); + return TypedAccepted(resource.Id); } [RestDeleteById] - public void DeleteTag(int id) + public NoContent DeleteTag(int id) { _tagService.Delete(id); + + return TypedResults.NoContent(); } [NonAction] diff --git a/src/Sonarr.Api.V5/Tags/TagDetailsController.cs b/src/Sonarr.Api.V5/Tags/TagDetailsController.cs index 5817f31ef..eb0a81577 100644 --- a/src/Sonarr.Api.V5/Tags/TagDetailsController.cs +++ b/src/Sonarr.Api.V5/Tags/TagDetailsController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.Tags; using Sonarr.Http; @@ -22,8 +24,8 @@ protected override TagDetailsResource GetResourceById(int id) [HttpGet] [Produces("application/json")] - public List GetAll() + public Ok> GetAll() { - return _tagService.Details().ToResource(); + return TypedResults.Ok(_tagService.Details().ToResource()); } } diff --git a/src/Sonarr.Api.V5/Update/UpdateController.cs b/src/Sonarr.Api.V5/Update/UpdateController.cs index a2d0cbabe..23c88e3a1 100644 --- a/src/Sonarr.Api.V5/Update/UpdateController.cs +++ b/src/Sonarr.Api.V5/Update/UpdateController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Core.Configuration; @@ -23,7 +25,7 @@ public UpdateController(IRecentUpdateProvider recentUpdateProvider, IUpdateHisto [HttpGet] [Produces("application/json")] - public List GetRecentUpdates() + public Ok> GetRecentUpdates() { var resources = _recentUpdateProvider.GetRecentUpdatePackages() .OrderByDescending(u => u.Version) @@ -48,7 +50,7 @@ public List GetRecentUpdates() if (!_configFileProvider.LogDbEnabled) { - return resources; + return TypedResults.Ok(resources); } var updateHistory = _updateHistoryService.InstalledSince(resources.Last().ReleaseDate); @@ -65,7 +67,7 @@ public List GetRecentUpdates() } } - return resources; + return TypedResults.Ok(resources); } } } diff --git a/src/Sonarr.Api.V5/Wanted/CutoffController.cs b/src/Sonarr.Api.V5/Wanted/CutoffController.cs index cf9f3cece..426a3d189 100644 --- a/src/Sonarr.Api.V5/Wanted/CutoffController.cs +++ b/src/Sonarr.Api.V5/Wanted/CutoffController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.Datastore; @@ -28,7 +30,7 @@ public CutoffController(IEpisodeCutoffService episodeCutoffService, [HttpGet] [Produces("application/json")] - public PagingResource GetCutoffUnmetEpisodes([FromQuery] PagingRequestResource paging, bool monitored = true, [FromQuery] CutoffSubresource[]? includeSubresources = null) + public Ok> GetCutoffUnmetEpisodes([FromQuery] PagingRequestResource paging, bool monitored = true, [FromQuery] CutoffSubresource[]? includeSubresources = null) { var pagingResource = new PagingResource(paging); var pagingSpec = pagingResource.MapToPagingSpec( @@ -56,6 +58,6 @@ public PagingResource GetCutoffUnmetEpisodes([FromQuery] Paging var resource = pagingSpec.ApplyToPage(_episodeCutoffService.EpisodesWhereCutoffUnmet, v => MapToResource(v, includeSeries, includeEpisodeFile, includeImages)); - return resource; + return TypedResults.Ok(resource); } } diff --git a/src/Sonarr.Api.V5/Wanted/MissingController.cs b/src/Sonarr.Api.V5/Wanted/MissingController.cs index e63d0bef5..dfffae74c 100644 --- a/src/Sonarr.Api.V5/Wanted/MissingController.cs +++ b/src/Sonarr.Api.V5/Wanted/MissingController.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.Datastore; @@ -24,7 +26,7 @@ public MissingController(IEpisodeService episodeService, [HttpGet] [Produces("application/json")] - public PagingResource GetMissingEpisodes([FromQuery] PagingRequestResource paging, bool monitored = true, bool includeSpecials = true, [FromQuery] MissingSubresource[]? includeSubresources = null) + public Ok> GetMissingEpisodes([FromQuery] PagingRequestResource paging, bool monitored = true, bool includeSpecials = true, [FromQuery] MissingSubresource[]? includeSubresources = null) { var pagingResource = new PagingResource(paging); var pagingSpec = pagingResource.MapToPagingSpec( @@ -51,6 +53,6 @@ public PagingResource GetMissingEpisodes([FromQuery] PagingRequ var resource = pagingSpec.ApplyToPage(spec => _episodeService.EpisodesWithoutFiles(spec, includeSpecials), v => MapToResource(v, includeSeries, false, includeImages)); - return resource; + return TypedResults.Ok(resource); } } diff --git a/src/Sonarr.Http/REST/RestController.cs b/src/Sonarr.Http/REST/RestController.cs index ee32c6a09..e9da21178 100644 --- a/src/Sonarr.Http/REST/RestController.cs +++ b/src/Sonarr.Http/REST/RestController.cs @@ -4,6 +4,8 @@ using System.Reflection; using FluentValidation; using FluentValidation.Results; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; @@ -50,15 +52,15 @@ protected RestController() [RestGetById] [Produces("application/json")] - public virtual ActionResult GetResourceByIdWithErrorHandler(int id) + public virtual Results, NotFound> GetResourceByIdWithErrorHandler(int id) { try { - return GetResourceById(id); + return TypedResults.Ok(GetResourceById(id)); } catch (ModelNotFoundException) { - return NotFound(); + return TypedResults.NotFound(); } } @@ -156,6 +158,20 @@ protected void ValidateResource(TResource resource, bool validateId = false, boo } } + protected Results, NotFound> TypedAccepted(int id) + { + var result = GetResourceById(id); + + return TypedResults.Accepted(Url.Action(nameof(GetResourceByIdWithErrorHandler), new { id }), result); + } + + protected Results, NotFound> TypedCreated(int id) + { + var result = GetResourceById(id); + + return TypedResults.Created(Url.Action(nameof(GetResourceByIdWithErrorHandler), new { id }), result); + } + protected ActionResult Accepted(int id) { var result = GetResourceById(id);