Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Notify Smoke Test / Notify Unit Tests (push) Has been cancelled
Notify Smoke Test / Notifier Service Tests (push) Has been cancelled
Notify Smoke Test / Notification Smoke Test (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
186 lines
6.9 KiB
C#
186 lines
6.9 KiB
C#
using System;
|
|
using System.Collections.Immutable;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace StellaOps.Scheduler.Models;
|
|
|
|
public sealed record PolicyRunJob(
|
|
string SchemaVersion,
|
|
string Id,
|
|
string TenantId,
|
|
string PolicyId,
|
|
int? PolicyVersion,
|
|
PolicyRunMode Mode,
|
|
PolicyRunPriority Priority,
|
|
int PriorityRank,
|
|
string? RunId,
|
|
string? RequestedBy,
|
|
string? CorrelationId,
|
|
ImmutableSortedDictionary<string, string>? Metadata,
|
|
PolicyRunInputs Inputs,
|
|
DateTimeOffset? QueuedAt,
|
|
PolicyRunJobStatus Status,
|
|
int AttemptCount,
|
|
DateTimeOffset? LastAttemptAt,
|
|
string? LastError,
|
|
DateTimeOffset CreatedAt,
|
|
DateTimeOffset UpdatedAt,
|
|
DateTimeOffset AvailableAt,
|
|
DateTimeOffset? SubmittedAt,
|
|
DateTimeOffset? CompletedAt,
|
|
string? LeaseOwner,
|
|
DateTimeOffset? LeaseExpiresAt,
|
|
bool CancellationRequested,
|
|
DateTimeOffset? CancellationRequestedAt,
|
|
string? CancellationReason,
|
|
DateTimeOffset? CancelledAt)
|
|
{
|
|
public string SchemaVersion { get; init; } = SchedulerSchemaVersions.EnsurePolicyRunJob(SchemaVersion);
|
|
|
|
public string Id { get; init; } = Validation.EnsureId(Id, nameof(Id));
|
|
|
|
public string TenantId { get; init; } = Validation.EnsureTenantId(TenantId, nameof(TenantId));
|
|
|
|
public string PolicyId { get; init; } = Validation.EnsureSimpleIdentifier(PolicyId, nameof(PolicyId));
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public int? PolicyVersion { get; init; } = EnsurePolicyVersion(PolicyVersion);
|
|
|
|
public PolicyRunMode Mode { get; init; } = Mode;
|
|
|
|
public PolicyRunPriority Priority { get; init; } = Priority;
|
|
|
|
public int PriorityRank { get; init; } = PriorityRank >= 0 ? PriorityRank : GetPriorityRank(Priority);
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public string? RunId { get; init; } = NormalizeRunId(RunId);
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public string? RequestedBy { get; init; } = Validation.TrimToNull(RequestedBy);
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public string? CorrelationId { get; init; } = Validation.TrimToNull(CorrelationId);
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public ImmutableSortedDictionary<string, string>? Metadata { get; init; } = NormalizeMetadata(Metadata);
|
|
|
|
public PolicyRunInputs Inputs { get; init; } = Inputs ?? throw new ArgumentNullException(nameof(Inputs));
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public DateTimeOffset? QueuedAt { get; init; } = Validation.NormalizeTimestamp(QueuedAt);
|
|
|
|
public PolicyRunJobStatus Status { get; init; } = Status;
|
|
|
|
public int AttemptCount { get; init; } = Validation.EnsureNonNegative(AttemptCount, nameof(AttemptCount));
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public DateTimeOffset? LastAttemptAt { get; init; } = Validation.NormalizeTimestamp(LastAttemptAt);
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public string? LastError { get; init; } = Validation.TrimToNull(LastError);
|
|
|
|
public DateTimeOffset CreatedAt { get; init; } = NormalizeTimestamp(CreatedAt, nameof(CreatedAt));
|
|
|
|
public DateTimeOffset UpdatedAt { get; init; } = NormalizeTimestamp(UpdatedAt, nameof(UpdatedAt));
|
|
|
|
public DateTimeOffset AvailableAt { get; init; } = NormalizeTimestamp(AvailableAt, nameof(AvailableAt));
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public DateTimeOffset? SubmittedAt { get; init; } = Validation.NormalizeTimestamp(SubmittedAt);
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public DateTimeOffset? CompletedAt { get; init; } = Validation.NormalizeTimestamp(CompletedAt);
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public string? LeaseOwner { get; init; } = Validation.TrimToNull(LeaseOwner);
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public DateTimeOffset? LeaseExpiresAt { get; init; } = Validation.NormalizeTimestamp(LeaseExpiresAt);
|
|
|
|
public bool CancellationRequested { get; init; } = CancellationRequested;
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public DateTimeOffset? CancellationRequestedAt { get; init; } = Validation.NormalizeTimestamp(CancellationRequestedAt);
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public string? CancellationReason { get; init; } = Validation.TrimToNull(CancellationReason);
|
|
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public DateTimeOffset? CancelledAt { get; init; } = Validation.NormalizeTimestamp(CancelledAt);
|
|
|
|
public PolicyRunRequest ToPolicyRunRequest(DateTimeOffset fallbackQueuedAt)
|
|
{
|
|
var queuedAt = QueuedAt ?? fallbackQueuedAt;
|
|
return new PolicyRunRequest(
|
|
TenantId,
|
|
PolicyId,
|
|
Mode,
|
|
Inputs,
|
|
Priority,
|
|
RunId,
|
|
PolicyVersion,
|
|
RequestedBy,
|
|
queuedAt,
|
|
CorrelationId,
|
|
Metadata);
|
|
}
|
|
|
|
private static int? EnsurePolicyVersion(int? value)
|
|
{
|
|
if (value is not null && value <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(PolicyVersion), value, "Policy version must be positive.");
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
private static string? NormalizeRunId(string? runId)
|
|
{
|
|
var trimmed = Validation.TrimToNull(runId);
|
|
return trimmed is null ? null : Validation.EnsureId(trimmed, nameof(runId));
|
|
}
|
|
|
|
private static ImmutableSortedDictionary<string, string>? NormalizeMetadata(ImmutableSortedDictionary<string, string>? metadata)
|
|
{
|
|
if (metadata is null || metadata.Count == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var builder = ImmutableSortedDictionary.CreateBuilder<string, string>(StringComparer.Ordinal);
|
|
foreach (var (key, value) in metadata)
|
|
{
|
|
var normalizedKey = Validation.TrimToNull(key);
|
|
var normalizedValue = Validation.TrimToNull(value);
|
|
if (normalizedKey is null || normalizedValue is null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
builder[normalizedKey.ToLowerInvariant()] = normalizedValue;
|
|
}
|
|
|
|
return builder.Count == 0 ? null : builder.ToImmutable();
|
|
}
|
|
|
|
private static int GetPriorityRank(PolicyRunPriority priority)
|
|
=> priority switch
|
|
{
|
|
PolicyRunPriority.Emergency => 2,
|
|
PolicyRunPriority.High => 1,
|
|
_ => 0
|
|
};
|
|
|
|
private static DateTimeOffset NormalizeTimestamp(DateTimeOffset value, string propertyName)
|
|
{
|
|
var normalized = Validation.NormalizeTimestamp(value);
|
|
if (normalized == default)
|
|
{
|
|
throw new ArgumentException($"{propertyName} must be a valid timestamp.", propertyName);
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
}
|