Files
ersatztv/ErsatzTV/Controllers/Api/Requests/RerunCollectionRequestMapping.cs
T
timothyandClaude Opus 4.8 dc459c6cd5
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m25s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m42s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
fix(api): reject unsupported CollectionType on rerun-collection create/update (#152 review)
The rerun request mapping's catch-all routed Playlist/RerunFirstRun/RerunRerun/
SearchQuery/Fake* to MediaItemId, so POST/PUT with those persisted a wrong-kind id
and returned 201 instead of 422. Guard both verbs with IsSupportedSelectionType.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 18:30:06 +02:00

77 lines
3.5 KiB
C#

using ErsatzTV.Application.MediaCollections;
using ErsatzTV.Application.MediaItems;
using ErsatzTV.Core.Domain;
namespace ErsatzTV.Controllers.Api.Requests;
// A rerun collection is a tagged union: CollectionType selects which of Collection /
// MultiCollection / SmartCollection / MediaItem is populated. The Create/Update handlers
// read ONLY the `.Id` (or `.MediaItemId`) off the chosen VM — verified in
// CreateRerunCollectionHandler.Validate / UpdateRerunCollectionHandler.ApplyUpdateRequest,
// both of which do `request.Collection?.Id`, `request.MultiCollection?.Id`,
// `request.SmartCollection?.Id`, `request.MediaItem?.MediaItemId` and nothing else. So we
// construct a minimal VM carrying just the selected id (defaults elsewhere) rather than
// doing extra lookup queries. The remaining VMs are left null (the handler expects that).
internal static class RerunCollectionRequestMapping
{
/// <summary>
/// The only <see cref="CollectionType" /> values valid as a rerun collection's selected
/// source. Excludes Playlist / RerunFirstRun / RerunRerun / SearchQuery / Fake* — those
/// are not media-item-backed collection types and must not be resolved via
/// <see cref="ResolveSelection" />. Callers (the controller) must check this first.
/// </summary>
public static bool IsSupportedSelectionType(CollectionType collectionType) =>
collectionType is CollectionType.Collection
or CollectionType.MultiCollection
or CollectionType.SmartCollection
or CollectionType.TelevisionShow
or CollectionType.TelevisionSeason
or CollectionType.Artist
or CollectionType.Movie
or CollectionType.Episode
or CollectionType.MusicVideo
or CollectionType.OtherVideo
or CollectionType.Song
or CollectionType.Image
or CollectionType.RemoteStream;
public static (MediaCollectionViewModel Collection,
MultiCollectionViewModel MultiCollection,
SmartCollectionViewModel SmartCollection,
NamedMediaItemViewModel MediaItem) ResolveSelection(CollectionType collectionType, int selectedId) =>
collectionType switch
{
CollectionType.Collection => (
new MediaCollectionViewModel(
CollectionType.Collection,
selectedId,
string.Empty,
false,
MediaItemState.Normal),
null,
null,
null),
CollectionType.MultiCollection => (
null,
new MultiCollectionViewModel(selectedId, string.Empty, [], []),
null,
null),
CollectionType.SmartCollection => (
null,
null,
new SmartCollectionViewModel(selectedId, string.Empty, string.Empty),
null),
// Any media-item-backed collection type (TelevisionShow, TelevisionSeason,
// Artist, Movie, Episode, MusicVideo, OtherVideo, Song, Image, ...) resolves to
// the MediaItem VM keyed by media item id. The caller (RerunCollectionController)
// has already validated CollectionType via IsSupportedSelectionType, so this
// catch-all only ever receives a real media-item type.
_ => (
null,
null,
null,
new NamedMediaItemViewModel(selectedId, string.Empty))
};
}