< Summary

Information
Class: Elsa.UserTasks.Contracts.UserTaskRevisionConflictException
Assembly: Elsa.UserTasks
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.UserTasks/Contracts/UserTaskContracts.cs
Line coverage
100%
Covered lines: 3
Uncovered lines: 0
Coverable lines: 3
Total lines: 190
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_TaskId()100%11100%
get_ExpectedRevision()100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.UserTasks/Contracts/UserTaskContracts.cs

#LineLine coverage
 1using System.Security.Claims;
 2using System.Text.Json;
 3using Elsa.UserTasks.Models;
 4
 5namespace Elsa.UserTasks.Contracts;
 6
 7public interface IUserTaskIdentityResolver
 8{
 9    ValueTask<UserTaskActor?> ResolveAsync(ClaimsPrincipal principal, CancellationToken cancellationToken = default);
 10}
 11
 12public interface IUserTaskAccessPolicy
 13{
 14    /// <summary>
 15    /// Builds the authorization predicate for a list request, or returns <c>null</c> when the actor may not
 16    /// list in the requested scope at all. Callers must treat <c>null</c> as a denial, never as "no filter".
 17    /// </summary>
 18    Task<UserTaskQueryScope?> CreateScopeAsync(UserTaskActor actor, UserTaskQueryScopeKind kind, CancellationToken cance
 19    Task<bool> AuthorizeAsync(UserTask task, UserTaskActor actor, UserTaskAccessOperation operation, CancellationToken c
 20}
 21
 22public interface IUserTaskParticipantDirectory
 23{
 24    Task<ParticipantSearchResult> SearchAsync(UserTaskParticipantQuery query, CancellationToken cancellationToken = defa
 25    Task<IReadOnlyCollection<ParticipantReference>> ResolveDisplayNamesAsync(IReadOnlyCollection<ParticipantReference> p
 26    Task<IReadOnlyCollection<ParticipantReference>> EnumerateGroupMembersAsync(ParticipantReference group, CancellationT
 27}
 28
 29public interface IUserTaskFormProvider
 30{
 31    string Name { get; }
 32    Task<ResolvedUserTaskForm?> ResolveAsync(UserTaskFormReference reference, CancellationToken cancellationToken = defa
 33    Task<UserTaskFormValidationResult> ValidateAndNormalizeAsync(ResolvedUserTaskForm form, string actionKey, JsonElemen
 34}
 35
 36/// <summary>
 37/// Signals that a task changed between being read and being saved. Every repository translates its own
 38/// store-specific concurrency failure into this type, so callers never have to know which provider is
 39/// installed to tell a lost optimistic-concurrency race from a genuine fault.
 40/// </summary>
 41public sealed class UserTaskRevisionConflictException(string taskId, int expectedRevision, Exception? innerException = n
 642    : Exception($"User task '{taskId}' changed before revision {expectedRevision} could be saved.", innerException)
 43{
 944    public string TaskId { get; } = taskId;
 945    public int ExpectedRevision { get; } = expectedRevision;
 46}
 47
 48public interface IUserTaskRepository
 49{
 50    Task<UserTask?> GetAsync(string tenantId, string taskId, CancellationToken cancellationToken = default);
 51    Task<UserTaskQueryResult> QueryAsync(UserTaskQuery query, CancellationToken cancellationToken = default);
 52    Task<UserTask?> FindByMaterializationKeyAsync(string tenantId, string key, CancellationToken cancellationToken = def
 53    Task<UserTask?> FindByBookmarkIdAsync(string tenantId, string bookmarkId, CancellationToken cancellationToken = defa
 54
 55    /// <summary>
 56    /// Resolves the task that owns an invitation by the invitation's stored token hash. Lookup is
 57    /// deliberately hash-keyed and tenant-agnostic: an anonymous caller presents only a secret, and the
 58    /// provider must never be asked to trust a caller-supplied tenant or task identifier.
 59    /// </summary>
 60    Task<(UserTask Task, UserTaskInvitation Invitation)?> FindByInvitationTokenHashAsync(string tokenHash, CancellationT
 61    /// <summary>Throws <see cref="UserTaskRevisionConflictException"/> when the stored revision has moved on.</summary>
 62    Task SaveAsync(UserTask task, int expectedRevision, CancellationToken cancellationToken = default);
 63    Task AddProjectionAsync(UserTask task, CancellationToken cancellationToken = default);
 64
 65    /// <summary>
 66    /// Appends one audit entry without touching the aggregate's revision. Audit is append-only and must not
 67    /// consume the optimistic-concurrency token: recording a read would otherwise invalidate the expected
 68    /// revision a client is already holding and turn its next command into a spurious conflict.
 69    /// </summary>
 70    Task AppendEventAsync(string tenantId, string taskId, UserTaskEvent @event, CancellationToken cancellationToken = de
 71    Task<bool> TryMutateAsync(string tenantId, string taskId, int expectedRevision, Func<UserTask, bool> mutation, Cance
 72}
 73
 74public interface IUserTaskManager
 75{
 76    Task<UserTaskProjectionResult> ProjectAsync(UserTaskMaterialization materialization, CancellationToken cancellationT
 77    /// <summary>Returns <c>null</c> when the actor may not list in the requested scope.</summary>
 78    Task<UserTaskQueryResultDto?> QueryAsync(UserTaskQuery query, UserTaskQueryScopeKind scope, UserTaskActor actor, Can
 79    Task<UserTaskDetail?> GetAsync(string tenantId, string taskId, UserTaskActor actor, CancellationToken cancellationTo
 80    Task<UserTaskCapabilities?> GetCapabilitiesAsync(string tenantId, string taskId, UserTaskActor actor, CancellationTo
 81    Task<UserTaskEventsResult?> GetEventsAsync(string tenantId, string taskId, string? cursor, int limit, UserTaskActor 
 82
 83    /// <summary>
 84    /// Discloses one masked form field after an explicit, audited request. Returns <c>null</c> when the task
 85    /// is invisible, the field is unknown, or the field was not marked revealable by its form provider.
 86    /// </summary>
 87    Task<JsonElement?> RevealFieldAsync(string tenantId, string taskId, string fieldKey, UserTaskActor actor, Cancellati
 88    Task<UserTaskOperationResult> ClaimAsync(string tenantId, string taskId, UserTaskMutationRequest request, UserTaskAc
 89    Task<UserTaskOperationResult> ReleaseAsync(string tenantId, string taskId, UserTaskMutationRequest request, UserTask
 90    Task<UserTaskOperationResult> AssignAsync(string tenantId, string taskId, UserTaskAssignRequest request, UserTaskAct
 91    Task<UserTaskOperationResult> UpdateSchedulingAsync(string tenantId, string taskId, UserTaskSchedulingUpdate request
 92    Task<UserTaskOperationResult> CompleteAsync(string tenantId, string taskId, UserTaskCompletionRequest request, UserT
 93    Task<UserTaskOperationResult> TimeoutAsync(string tenantId, string taskId, int expectedRevision, DateTimeOffset now,
 94    Task<UserTaskOperationResult> CancelAsync(string tenantId, string taskId, UserTaskCancelRequest request, UserTaskAct
 95    Task<UserTaskOperationResult> RetryResolutionAsync(string tenantId, string taskId, UserTaskMutationRequest request, 
 96}
 97
 98public interface IUserTaskProjectionService
 99{
 100    Task ProjectCommittedBookmarksAsync(IReadOnlyCollection<UserTaskMaterialization> materializations, CancellationToken
 101    Task FinalizeBookmarkRemovalAsync(UserTaskBookmarkRemoval removal, CancellationToken cancellationToken = default);
 102}
 103
 104public interface IUserTaskReconciler
 105{
 106    Task<UserTaskReconciliationResult> ReconcileAsync(UserTaskReconciliationRequest request, CancellationToken cancellat
 107}
 108
 109public interface IUserTaskDueService
 110{
 111    Task<int> MarkOverdueAsync(string tenantId, DateTimeOffset? now = null, CancellationToken cancellationToken = defaul
 112}
 113
 114public interface IUserTaskWorkflowResumer
 115{
 116    Task ResumeAsync(UserTask task, UserTaskStimulus stimulus, CancellationToken cancellationToken = default);
 117}
 118
 119public interface IUserTaskInvitationDispatcher
 120{
 121    Task DispatchAsync(UserTaskInvitationDelivery delivery, CancellationToken cancellationToken = default);
 122}
 123
 124public interface IUserTaskInvitationVerifier
 125{
 126    Task<UserTaskInvitationVerificationResult> VerifyAsync(UserTaskInvitationChallenge challenge, CancellationToken canc
 127}
 128
 129/// <summary>
 130/// Issues and resolves revocable, task-scoped guest sessions. Implementations must store only a hash of the
 131/// session credential and must invalidate a session as soon as its task closes.
 132/// </summary>
 133public interface IUserTaskGuestSessionIssuer
 134{
 135    Task<GuestSessionResult> IssueAsync(UserTaskInvitation invitation, ParticipantReference subject, CancellationToken c
 136
 137    /// <summary>Resolves a presented credential, or returns <c>null</c> for unknown, expired, or revoked sessions.</sum
 138    Task<UserTaskGuestSession?> ResolveAsync(string credential, CancellationToken cancellationToken = default);
 139
 140    /// <summary>Revokes every session issued for a task. Called when the task reaches a terminal state.</summary>
 141    Task RevokeForTaskAsync(string tenantId, string taskId, CancellationToken cancellationToken = default);
 142
 143    /// <summary>
 144    /// Revokes the sessions issued from one invitation. This is what makes a guest credential withdrawable:
 145    /// consuming an invitation is what creates the session, so revoking the invitation must kill it too.
 146    /// </summary>
 147    Task RevokeForInvitationAsync(string tenantId, string invitationId, CancellationToken cancellationToken = default);
 148}
 149
 150/// <summary>
 151/// Rate limiter for the anonymous invitation surface. The default implementation is a per-process sliding
 152/// window; hosts running multiple replicas should replace it with a shared-store implementation.
 153/// </summary>
 154public interface IUserTaskInvitationRateLimiter
 155{
 156    /// <summary>Returns <c>false</c> when the caller has exhausted its budget and must receive a 429.</summary>
 157    ValueTask<bool> TryAcquireAsync(string partitionKey, CancellationToken cancellationToken = default);
 158}
 159
 160/// <summary>
 161/// Holds invitation secrets between issuance and successful delivery. Entries are encrypted at rest through
 162/// ASP.NET Core Data Protection and removed once delivery succeeds or the invitation expires.
 163/// </summary>
 164public interface IUserTaskInvitationOutbox
 165{
 166    Task EnqueueAsync(UserTaskInvitationDelivery delivery, CancellationToken cancellationToken = default);
 167    Task<IReadOnlyCollection<UserTaskInvitationDelivery>> DequeueDueAsync(int maxCount, CancellationToken cancellationTo
 168    Task CompleteAsync(string deliveryId, CancellationToken cancellationToken = default);
 169    Task RescheduleAsync(string deliveryId, DateTimeOffset notBefore, CancellationToken cancellationToken = default);
 170}
 171
 172public interface IUserTaskInvitationService
 173{
 174    Task<UserTaskInvitationIssueResult?> IssueAsync(string tenantId, string taskId, UserTaskInvitationIssueRequest reque
 175    Task<IReadOnlyCollection<UserTaskInvitationSummary>?> ListAsync(string tenantId, string taskId, UserTaskActor actor,
 176    Task<bool> RevokeAsync(string tenantId, string taskId, string invitationId, int expectedRevision, UserTaskActor acto
 177
 178    /// <summary>
 179    /// Describes the challenge an anonymous holder must answer. The descriptor is deliberately generic so a
 180    /// caller cannot distinguish a missing, expired, consumed, or revoked invitation from a valid one.
 181    /// </summary>
 182    Task<UserTaskInvitationChallengeDescriptor> DescribeAsync(string token, CancellationToken cancellationToken = defaul
 183
 184    Task<UserTaskInvitationVerificationResultWithSession> VerifyAsync(UserTaskInvitationChallenge challenge, Cancellatio
 185}
 186
 187public interface IUserTaskNotificationSink
 188{
 189    Task PublishAsync(UserTaskLifecycleNotification notification, CancellationToken cancellationToken = default);
 190}