| | | 1 | | using Elsa.Workflows.Runtime; |
| | | 2 | | |
| | | 3 | | namespace Elsa.Workflows.Api.Endpoints.RuntimeAdmin; |
| | | 4 | | |
| | | 5 | | /// <summary>Body for the pause endpoint. The reason is a free-form human-readable string captured on the audit event.</ |
| | | 6 | | public class PauseRequest |
| | | 7 | | { |
| | | 8 | | public string? Reason { get; set; } |
| | | 9 | | } |
| | | 10 | | |
| | | 11 | | /// <summary>Body for the resume endpoint. No fields today — kept as a class so the endpoint signature is uniform.</summ |
| | | 12 | | public class ResumeRequest |
| | | 13 | | { |
| | | 14 | | } |
| | | 15 | | |
| | | 16 | | /// <summary>Body for the operator-force drain endpoint.</summary> |
| | | 17 | | public class ForceDrainRequest |
| | | 18 | | { |
| | | 19 | | public string? Reason { get; set; } |
| | | 20 | | } |
| | | 21 | | |
| | | 22 | | /// <summary>Composite quiescence state surfaced by status / pause / resume responses.</summary> |
| | | 23 | | public class QuiescenceStateDto |
| | | 24 | | { |
| | | 25 | | public string Reason { get; set; } = null!; |
| | | 26 | | public bool IsAcceptingNewWork { get; set; } |
| | | 27 | | public DateTimeOffset? PausedAt { get; set; } |
| | | 28 | | public DateTimeOffset? DrainStartedAt { get; set; } |
| | | 29 | | public string? PauseReasonText { get; set; } |
| | | 30 | | public string? PauseRequestedBy { get; set; } |
| | | 31 | | public string GenerationId { get; set; } = null!; |
| | | 32 | | } |
| | | 33 | | |
| | | 34 | | /// <summary>Per-ingress-source state surfaced by status / pause / resume responses.</summary> |
| | | 35 | | public class IngressSourceStateDto |
| | | 36 | | { |
| | | 37 | | public string Name { get; set; } = null!; |
| | | 38 | | public string State { get; set; } = null!; |
| | | 39 | | public string? LastError { get; set; } |
| | | 40 | | public DateTimeOffset? LastTransitionAt { get; set; } |
| | | 41 | | } |
| | | 42 | | |
| | | 43 | | /// <summary>Status / pause / resume response. Idempotent endpoints return the post-request state.</summary> |
| | | 44 | | public class StatusResponse |
| | | 45 | | { |
| | | 46 | | public QuiescenceStateDto State { get; set; } = null!; |
| | | 47 | | public List<IngressSourceStateDto> Sources { get; set; } = new(); |
| | | 48 | | public int ActiveExecutionCycleCount { get; set; } |
| | | 49 | | } |
| | | 50 | | |
| | | 51 | | /// <summary>Force-drain outcome surfaced by the force endpoint.</summary> |
| | | 52 | | public class DrainOutcomeDto |
| | | 53 | | { |
| | | 54 | | public string OverallResult { get; set; } = null!; |
| | | 55 | | public DateTimeOffset StartedAt { get; set; } |
| | | 56 | | public DateTimeOffset CompletedAt { get; set; } |
| | | 57 | | public TimeSpan PausePhaseDuration { get; set; } |
| | | 58 | | public TimeSpan WaitPhaseDuration { get; set; } |
| | | 59 | | public List<IngressSourceStateDto> Sources { get; set; } = new(); |
| | | 60 | | public int ExecutionCyclesForceCancelledCount { get; set; } |
| | | 61 | | public List<string> ForceCancelledInstanceIds { get; set; } = new(); |
| | | 62 | | } |
| | | 63 | | |
| | | 64 | | /// <summary>Force-drain endpoint response.</summary> |
| | | 65 | | public class ForceDrainResponse |
| | | 66 | | { |
| | | 67 | | public DrainOutcomeDto Outcome { get; set; } = null!; |
| | | 68 | | } |
| | | 69 | | |
| | | 70 | | /// <summary>Returned by resume/force endpoints when a drain is already in progress.</summary> |
| | | 71 | | public class ConflictResponse |
| | | 72 | | { |
| | | 73 | | public string Code { get; set; } = null!; |
| | | 74 | | public StatusResponse State { get; set; } = null!; |
| | | 75 | | } |
| | | 76 | | |
| | | 77 | | internal static class StatusResponseFactory |
| | | 78 | | { |
| | 0 | 79 | | public static QuiescenceStateDto MapState(QuiescenceState state) => new() |
| | 0 | 80 | | { |
| | 0 | 81 | | Reason = state.Reason.ToString(), |
| | 0 | 82 | | IsAcceptingNewWork = state.IsAcceptingNewWork, |
| | 0 | 83 | | PausedAt = state.PausedAt, |
| | 0 | 84 | | DrainStartedAt = state.DrainStartedAt, |
| | 0 | 85 | | PauseReasonText = state.PauseReasonText, |
| | 0 | 86 | | PauseRequestedBy = state.PauseRequestedBy, |
| | 0 | 87 | | GenerationId = state.GenerationId, |
| | 0 | 88 | | }; |
| | | 89 | | |
| | 0 | 90 | | public static IngressSourceStateDto MapSource(IngressSourceSnapshot s) => new() |
| | 0 | 91 | | { |
| | 0 | 92 | | Name = s.Name, |
| | 0 | 93 | | State = s.State.ToString(), |
| | 0 | 94 | | LastError = s.LastError?.Message, |
| | 0 | 95 | | LastTransitionAt = s.LastTransitionAt, |
| | 0 | 96 | | }; |
| | | 97 | | |
| | 0 | 98 | | public static StatusResponse Build(RuntimeAdminStatus status) => new() |
| | 0 | 99 | | { |
| | 0 | 100 | | State = MapState(status.State), |
| | 0 | 101 | | Sources = status.Sources.Select(MapSource).ToList(), |
| | 0 | 102 | | ActiveExecutionCycleCount = status.ActiveExecutionCycleCount, |
| | 0 | 103 | | }; |
| | | 104 | | } |