feat: validate single-client broadcast operations
Build web service image / build (push) Successful in 1m9s
Build web service image / build (push) Successful in 1m9s
This commit is contained in:
@@ -4,7 +4,7 @@ namespace WxAgent.Service;
|
||||
|
||||
public sealed record OperationRecord(string Id, string PrincipalId, string AccountId, string Capability,
|
||||
string State, string Stage, string CorrelationId, DateTimeOffset CreatedAt, DateTimeOffset ExpiresAt,
|
||||
string? ErrorCode, bool HasSideEffects);
|
||||
string? ErrorCode, bool HasSideEffects, string? Details = null);
|
||||
|
||||
public sealed record OperationSummary(string Id, string AccountId, string Capability,
|
||||
string State, string Stage, string CorrelationId, DateTimeOffset CreatedAt, DateTimeOffset ExpiresAt,
|
||||
@@ -37,10 +37,12 @@ public sealed class OperationStore : IDisposable
|
||||
stage='restart', error='AgentRestarted' WHERE state='Running';
|
||||
UPDATE operations SET state='Cancelled', stage='restart', error='AgentRestarted' WHERE state='Queued';
|
||||
""");
|
||||
try { Execute("ALTER TABLE operations ADD COLUMN details TEXT"); }
|
||||
catch (SqliteException exception) when (exception.SqliteErrorCode == 1) { }
|
||||
}
|
||||
|
||||
public (OperationRecord Record, bool Created) Enqueue(string principal, string account, string capability,
|
||||
string? idempotency, string digest, bool sideEffects, TimeSpan budget, int capacity)
|
||||
string? idempotency, string digest, bool sideEffects, TimeSpan budget, int capacity, string? details = null)
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
@@ -67,10 +69,10 @@ public sealed class OperationStore : IDisposable
|
||||
throw new ServiceException("QueueFull", 429, "Agent queue is full.");
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var operation = new OperationRecord(Guid.NewGuid().ToString("N"), principal, account, capability,
|
||||
"Queued", "queued", Guid.NewGuid().ToString("N"), now, now.Add(budget), null, sideEffects);
|
||||
"Queued", "queued", Guid.NewGuid().ToString("N"), now, now.Add(budget), null, sideEffects, details);
|
||||
using var insert = database.CreateCommand();
|
||||
insert.Transaction = transaction;
|
||||
insert.CommandText = "INSERT INTO operations VALUES ($id,$p,$a,$c,'Queued','queued',$correlation,$created,$expires,NULL,$effects,$key,$digest)";
|
||||
insert.CommandText = "INSERT INTO operations (id,principal,account,capability,state,stage,correlation,created,expires,error,side_effects,idempotency,digest,details) VALUES ($id,$p,$a,$c,'Queued','queued',$correlation,$created,$expires,NULL,$effects,$key,$digest,$details)";
|
||||
insert.Parameters.AddWithValue("$id", operation.Id);
|
||||
insert.Parameters.AddWithValue("$p", principal);
|
||||
insert.Parameters.AddWithValue("$a", account);
|
||||
@@ -81,6 +83,7 @@ public sealed class OperationStore : IDisposable
|
||||
insert.Parameters.AddWithValue("$effects", sideEffects ? 1 : 0);
|
||||
insert.Parameters.AddWithValue("$key", (object?)idempotency ?? DBNull.Value);
|
||||
insert.Parameters.AddWithValue("$digest", digest);
|
||||
insert.Parameters.AddWithValue("$details", (object?)details ?? DBNull.Value);
|
||||
insert.ExecuteNonQuery();
|
||||
transaction.Commit();
|
||||
return (operation, true);
|
||||
@@ -122,16 +125,17 @@ public sealed class OperationStore : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public void Transition(string id, string state, string stage, string? error = null)
|
||||
public void Transition(string id, string state, string stage, string? error = null, string? details = null)
|
||||
{
|
||||
lock (gate)
|
||||
{
|
||||
using var command = database.CreateCommand();
|
||||
command.CommandText = "UPDATE operations SET state=$state,stage=$stage,error=$error WHERE id=$id AND state IN ('Queued','Running')";
|
||||
command.CommandText = "UPDATE operations SET state=$state,stage=$stage,error=$error,details=COALESCE($details,details) WHERE id=$id AND state IN ('Queued','Running')";
|
||||
command.Parameters.AddWithValue("$id", id);
|
||||
command.Parameters.AddWithValue("$state", state);
|
||||
command.Parameters.AddWithValue("$stage", stage);
|
||||
command.Parameters.AddWithValue("$error", (object?)error ?? DBNull.Value);
|
||||
command.Parameters.AddWithValue("$details", (object?)details ?? DBNull.Value);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
@@ -139,7 +143,8 @@ public sealed class OperationStore : IDisposable
|
||||
private static OperationRecord Read(SqliteDataReader reader) => new(reader.GetString(0), reader.GetString(1), reader.GetString(2),
|
||||
reader.GetString(3), reader.GetString(4), reader.GetString(5), reader.GetString(6),
|
||||
DateTimeOffset.Parse(reader.GetString(7), System.Globalization.CultureInfo.InvariantCulture),
|
||||
DateTimeOffset.Parse(reader.GetString(8), System.Globalization.CultureInfo.InvariantCulture), reader.IsDBNull(9) ? null : reader.GetString(9), reader.GetInt64(10) != 0);
|
||||
DateTimeOffset.Parse(reader.GetString(8), System.Globalization.CultureInfo.InvariantCulture), reader.IsDBNull(9) ? null : reader.GetString(9), reader.GetInt64(10) != 0,
|
||||
reader.IsDBNull(13) ? null : reader.GetString(13));
|
||||
|
||||
private void Execute(string sql)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user