up
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
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
This commit is contained in:
@@ -1,185 +1,185 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user