120 lines
4.6 KiB
C#
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;
|
|
}
|
|
}
|