< Summary

Information
Class: Elsa.UserTasks.Activities.UserTaskActionDefinition
Assembly: Elsa.UserTasks
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.UserTasks/Activities/UserTask.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 10
Coverable lines: 10
Total lines: 144
Line coverage: 0%
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
get_Key()100%210%
get_Label()100%210%
get_Metadata()100%210%
.ctor()100%210%
.ctor(...)100%210%
Materialize(...)100%210%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.UserTasks/Activities/UserTask.cs

#LineLine coverage
 1using System.Runtime.CompilerServices;
 2using System.Text.Json;
 3using Elsa.Expressions.Models;
 4using Elsa.Extensions;
 5using Elsa.UserTasks.Contracts;
 6using Elsa.UserTasks.Models;
 7using Elsa.UserTasks.Options;
 8using Elsa.Workflows;
 9using Elsa.Workflows.Attributes;
 10using Elsa.Workflows.Memory;
 11using Elsa.Workflows.Models;
 12using Microsoft.Extensions.Options;
 13
 14namespace Elsa.UserTasks.Activities;
 15
 16/// <summary>
 17/// Creates a workflow-bound human task and suspends the workflow until a governed outcome resumes it.
 18/// </summary>
 19[Activity("Elsa", "User Tasks", "Creates a user task and waits for a human outcome.", Kind = ActivityKind.Task)]
 20public sealed class UserTask : Activity<UserTaskResult>
 21{
 22    public const string InputKey = "UserTaskInput";
 23
 24    [Input(Description = "The task title.")]
 25    public Input<string> Title { get; set; } = null!;
 26    [Input] public Input<string?> Summary { get; set; } = null!;
 27    [Input] public Input<string?> Reference { get; set; } = null!;
 28    [Input] public Input<IReadOnlyCollection<string>?> Tags { get; set; } = null!;
 29    [Input] public Input<string?> TaskType { get; set; } = null!;
 30    [Input] public Input<ParticipantReference?> Requester { get; set; } = null!;
 31    [Input] public Input<ParticipantReference?> Assignee { get; set; } = null!;
 32    [Input] public Input<IReadOnlyCollection<ParticipantReference>?> CandidateUsers { get; set; } = null!;
 33    [Input] public Input<IReadOnlyCollection<ParticipantReference>?> CandidateGroups { get; set; } = null!;
 34    [Input] public Input<IReadOnlyCollection<ParticipantReference>?> ExcludedUsers { get; set; } = null!;
 35    [Input] public Input<UserTaskMembershipResolutionMode> MembershipResolutionMode { get; set; } = new(new Literal<User
 36    [Input] public Input<bool> AllowManagerExclusionOverride { get; set; } = null!;
 37    [Input] public Input<int> Priority { get; set; } = new(new Literal<int>(50));
 38    [Input] public Input<DateTimeOffset?> DueAt { get; set; } = null!;
 39    [Input] public Input<string?> Instructions { get; set; } = null!;
 40    [Input] public Input<JsonElement?> TaskData { get; set; } = null!;
 41    [Input] public Input<UserTaskFormReference?> Form { get; set; } = null!;
 42    [Input(Description = "Completion actions. Keys are literal; labels may use expressions.", DefaultSyntax = "Literal",
 43    public Input<IReadOnlyCollection<UserTaskActionDefinition>?> ActionDefinitions { get; set; } = null!;
 44    [Input] public Input<IReadOnlyCollection<UserTaskInvitationDefinition>?> Invitations { get; set; } = null!;
 45    [Input] public Input<bool> EnableTimeoutOutcome { get; set; } = null!;
 46    [Input] public Input<bool> EnableCancellationOutcome { get; set; } = null!;
 47    [Output] public Output<string> TaskId { get; set; } = null!;
 48
 49    public UserTask()
 50    {
 51    }
 52
 53    public UserTask(string title, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(so
 54    {
 55        Title = new Input<string>(new Literal<string>(title));
 56    }
 57
 58    protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
 59    {
 60        var identityGenerator = context.GetRequiredService<IIdentityGenerator>();
 61        var taskId = identityGenerator.GenerateId();
 62        var bookmarkId = identityGenerator.GenerateId();
 63        var options = context.GetService<IOptions<UserTasksOptions>>()?.Value ?? new UserTasksOptions();
 64        var definition = new UserTaskDefinitionSnapshot
 65        {
 66            Title = Title.Get(context),
 67            Summary = Summary.GetOrDefault(context),
 68            Reference = Reference.GetOrDefault(context),
 69            Tags = Tags.GetOrDefault(context) ?? [],
 70            TaskType = TaskType.GetOrDefault(context),
 71            Requester = Requester.GetOrDefault(context),
 72            Assignee = Assignee.GetOrDefault(context),
 73            CandidateUsers = CandidateUsers.GetOrDefault(context) ?? [],
 74            CandidateGroups = CandidateGroups.GetOrDefault(context) ?? [],
 75            ExcludedUsers = ExcludedUsers.GetOrDefault(context) ?? [],
 76            MembershipResolutionMode = MembershipResolutionMode.GetOrDefault(context),
 77            AllowManagerExclusionOverride = AllowManagerExclusionOverride.GetOrDefault(context),
 78            Priority = Priority.GetOrDefault(context),
 79            DueAt = DueAt.GetOrDefault(context),
 80            Instructions = Instructions.GetOrDefault(context),
 81            TaskData = TaskData.GetOrDefault(context),
 82            FormReference = Form.GetOrDefault(context),
 83            Actions = (ActionDefinitions.GetOrDefault(context) ?? []).Select(x => x.Materialize(context)).ToArray(),
 84            Invitations = Invitations.GetOrDefault(context) ?? [],
 85            EnableTimeoutOutcome = EnableTimeoutOutcome.GetOrDefault(context),
 86            EnableCancellationOutcome = EnableCancellationOutcome.GetOrDefault(context)
 87        };
 88        var workflow = context.WorkflowExecutionContext.Workflow;
 89        var materialization = new UserTaskMaterialization(
 90            // The workflow's own tenant wins; the configured default is only a fallback for single-tenant hosts.
 91            workflow.Identity.TenantId ?? options.DefaultTenantId,
 92            workflow.Identity.DefinitionId,
 93            context.WorkflowExecutionContext.Id,
 94            context.Id,
 95            bookmarkId,
 96            definition,
 97            [],
 98            [],
 99            context.WorkflowExecutionContext.SystemClock.UtcNow,
 100            taskId,
 101            workflow.WorkflowMetadata.Name,
 102            workflow.Identity.Version,
 103            context.WorkflowExecutionContext.CorrelationId);
 104        context.CreateBookmark(new CreateBookmarkArgs
 105        {
 106            BookmarkId = bookmarkId,
 107            BookmarkName = nameof(UserTask),
 108            Stimulus = materialization,
 109            Callback = ResumeAsync,
 110            IncludeActivityInstanceId = false
 111        });
 112        TaskId.Set(context, taskId);
 113    }
 114
 115    private async ValueTask ResumeAsync(ActivityExecutionContext context)
 116    {
 117        var stimulus = context.GetWorkflowInput<UserTaskStimulus>(InputKey);
 118        Result?.Set(context, new UserTaskResult(stimulus.ActionKey, stimulus.CompletionData, stimulus.CompletedBy, stimu
 119        await context.CompleteActivityAsync();
 120    }
 121}
 122
 123/// <summary>
 124/// Design-time action definition. Key is deliberately a plain literal so workflow expressions cannot
 125/// change the contract of an already activated task; only the label is expression-capable.
 126/// </summary>
 127public sealed class UserTaskActionDefinition
 128{
 0129    public string Key { get; set; } = "";
 0130    public Input<string> Label { get; set; } = null!;
 0131    public IReadOnlyDictionary<string, object?>? Metadata { get; set; }
 132
 0133    public UserTaskActionDefinition()
 134    {
 0135    }
 136
 0137    public UserTaskActionDefinition(string key, string label)
 138    {
 0139        Key = key;
 0140        Label = new Input<string>(new Literal<string>(label));
 0141    }
 142
 0143    internal UserTaskAction Materialize(ActivityExecutionContext context) => new(Key, Label.Get(context), Metadata);
 144}