Files
git.stella-ops.org/src/JobEngine/StellaOps.TaskRunner/StellaOps.TaskRunner.Infrastructure/Execution/FilePackRunApprovalStore.cs

120 lines
4.6 KiB
C#

using StellaOps.TaskRunner.Core.Execution;
using System.Text.Json;
using System.Text.Json.Nodes;
namespace StellaOps.TaskRunner.Infrastructure.Execution;
public sealed class FilePackRunApprovalStore : IPackRunApprovalStore
{
private readonly string rootPath;
private readonly JsonSerializerOptions serializerOptions = new(JsonSerializerDefaults.Web)
{
WriteIndented = true
};
public FilePackRunApprovalStore(string rootPath)
{
ArgumentException.ThrowIfNullOrWhiteSpace(rootPath);
this.rootPath = rootPath;
Directory.CreateDirectory(rootPath);
}
public Task SaveAsync(string runId, IReadOnlyList<PackRunApprovalState> approvals, CancellationToken cancellationToken)
{
var path = GetFilePath(runId);
var json = SerializeApprovals(approvals);
File.WriteAllText(path, json);
return Task.CompletedTask;
}
public Task<IReadOnlyList<PackRunApprovalState>> GetAsync(string runId, CancellationToken cancellationToken)
{
var path = GetFilePath(runId);
if (!File.Exists(path))
{
return Task.FromResult((IReadOnlyList<PackRunApprovalState>)Array.Empty<PackRunApprovalState>());
}
var json = File.ReadAllText(path);
var approvals = DeserializeApprovals(json);
return Task.FromResult((IReadOnlyList<PackRunApprovalState>)approvals);
}
public async Task UpdateAsync(string runId, PackRunApprovalState approval, CancellationToken cancellationToken)
{
var approvals = (await GetAsync(runId, cancellationToken).ConfigureAwait(false)).ToList();
var index = approvals.FindIndex(existing => string.Equals(existing.ApprovalId, approval.ApprovalId, StringComparison.Ordinal));
if (index < 0)
{
throw new InvalidOperationException($"Approval '{approval.ApprovalId}' not found for run '{runId}'.");
}
approvals[index] = approval;
await SaveAsync(runId, approvals, cancellationToken).ConfigureAwait(false);
}
private string GetFilePath(string runId)
{
var safeFile = $"{runId}.json";
return Path.Combine(rootPath, safeFile);
}
private string SerializeApprovals(IReadOnlyList<PackRunApprovalState> approvals)
{
var array = new JsonArray();
foreach (var approval in approvals)
{
var node = new JsonObject
{
["approvalId"] = approval.ApprovalId,
["status"] = approval.Status.ToString(),
["requestedAt"] = approval.RequestedAt,
["actorId"] = approval.ActorId,
["completedAt"] = approval.CompletedAt,
["summary"] = approval.Summary,
["requiredGrants"] = new JsonArray(approval.RequiredGrants.Select(grant => (JsonNode)grant).ToArray()),
["stepIds"] = new JsonArray(approval.StepIds.Select(step => (JsonNode)step).ToArray()),
["messages"] = new JsonArray(approval.Messages.Select(message => (JsonNode)message).ToArray()),
["reasonTemplate"] = approval.ReasonTemplate
};
array.Add(node);
}
return array.ToJsonString(serializerOptions);
}
private static IReadOnlyList<PackRunApprovalState> DeserializeApprovals(string json)
{
var array = JsonNode.Parse(json)?.AsArray() ?? new JsonArray();
var list = new List<PackRunApprovalState>(array.Count);
foreach (var entry in array)
{
if (entry is not JsonObject obj)
{
continue;
}
var requiredGrants = obj["requiredGrants"]?.AsArray()?.Select(node => node!.GetValue<string>()).ToList() ?? new List<string>();
var stepIds = obj["stepIds"]?.AsArray()?.Select(node => node!.GetValue<string>()).ToList() ?? new List<string>();
var messages = obj["messages"]?.AsArray()?.Select(node => node!.GetValue<string>()).ToList() ?? new List<string>();
Enum.TryParse(obj["status"]?.GetValue<string>(), ignoreCase: true, out PackRunApprovalStatus status);
list.Add(new PackRunApprovalState(
obj["approvalId"]?.GetValue<string>() ?? string.Empty,
requiredGrants,
stepIds,
messages,
obj["reasonTemplate"]?.GetValue<string>(),
obj["requestedAt"]?.GetValue<DateTimeOffset>() ?? DateTimeOffset.UtcNow,
status,
obj["actorId"]?.GetValue<string>(),
obj["completedAt"]?.GetValue<DateTimeOffset?>(),
obj["summary"]?.GetValue<string>()));
}
return list;
}
}