using ErsatzTV.Core.Domain; using ErsatzTV.Core.Errors; namespace ErsatzTV.Core; public static class VersionedAggregateExtensions { /// /// Optimistic-concurrency pre-check (issue #253). When the caller supplied one or more expected /// versions (the strong If-Match entity-tags), fail with /// (→ 412) unless the loaded aggregate's version is one of them (RFC 7232 §3.1: any strong match /// proceeds). An empty set never matches → always 412 (a weak/non-canonical tag was sent). /// An absent expectation is a force-write (Phase 1 back-compat). This returns a standalone /// so the 412 is introduced AFTER the validation pipeline and never /// flattened to 422 by (see api-conventions §7a). /// public static Either CheckVersion(this T aggregate, Option> expectedVersions) where T : IVersionedAggregate => expectedVersions.Match( Some: versions => versions.Contains(aggregate.Version) ? Right(aggregate) : Left(new PreconditionFailedError( "The resource was modified by another request. Reload and try again.")), None: () => Right(aggregate)); }