V13-FE-011: finalize search list layout slice
This commit is contained in:
+47
@@ -0,0 +1,47 @@
|
||||
using System.Text.Json;
|
||||
using Dapper;
|
||||
using FastEndpoints;
|
||||
using Npgsql;
|
||||
using Kbx.Shared.Runtime;
|
||||
using Shared.Problems;
|
||||
|
||||
namespace Kbx.Modules.Common.Integrations.Attempts.Retry;
|
||||
|
||||
public sealed record Request(string? IdempotencyKey = null);
|
||||
public sealed record Response(Guid AttemptId,string State,string Message);
|
||||
|
||||
public sealed class Endpoint(NpgsqlDataSource dataSource):Endpoint<Request,object>
|
||||
{
|
||||
private const string OperationId = "common.integrations.retryAttempt";
|
||||
public override void Configure(){Post("/api/integrations/attempts/{attemptId:guid}/retry");Permissions("common.integration.retry");}
|
||||
|
||||
public override async Task HandleAsync(Request req,CancellationToken ct)
|
||||
{
|
||||
var attemptId=Route<Guid>("attemptId");
|
||||
var key=HttpContext.Request.Headers["Idempotency-Key"].FirstOrDefault()??req.IdempotencyKey;
|
||||
if(string.IsNullOrWhiteSpace(key)){
|
||||
await Send.ResponseAsync(KbxValidationProblem.Create(new KbxValidationError(null,null,"IDEMPOTENCY_KEY_REQUIRED","안전한 재처리를 위해 Idempotency-Key가 필요합니다.")),400,cancellation:ct);return;
|
||||
}
|
||||
await using var c=await dataSource.OpenConnectionAsync(ct);
|
||||
await using var tx=await c.BeginTransactionAsync(ct);
|
||||
var replay=await c.QuerySingleOrDefaultAsync<string?>(new CommandDefinition(
|
||||
"select response_json::text from kbx.command_receipts where operation_id=@OperationId and idempotency_key=@Key",
|
||||
new { OperationId, Key=key },tx,cancellationToken:ct));
|
||||
if(replay is not null){await tx.RollbackAsync(ct);await Send.OkAsync(JsonSerializer.Deserialize<Response>(replay)!,ct);return;}
|
||||
|
||||
const string sql="""
|
||||
update kbx.integration_attempts
|
||||
set state='retrying', next_retry_at=now(), manual_retry_key=@Key
|
||||
where id=@Id and tenant_id=@TenantId and state='failed'
|
||||
returning id;
|
||||
""";
|
||||
var id=await c.ExecuteScalarAsync<Guid?>(new CommandDefinition(sql,new{Id=attemptId,TenantId=RuntimeIdentity.TenantId(User),Key=key},tx,cancellationToken:ct));
|
||||
if(id is null){await tx.RollbackAsync(ct);await Send.ResponseAsync(KbxNotFoundProblem.Create("INTEGRATION_ATTEMPT_NOT_RETRYABLE","재처리할 수 있는 연계 실패를 찾지 못했습니다."),404,cancellation:ct);return;}
|
||||
|
||||
var response=new Response(id.Value,"retrying","재처리를 예약했습니다.");
|
||||
await c.ExecuteAsync(new CommandDefinition("insert into kbx.command_receipts(operation_id,idempotency_key,response_json) values(@OperationId,@Key,cast(@Json as jsonb))",
|
||||
new { OperationId, Key=key, Json=JsonSerializer.Serialize(response) },tx,cancellationToken:ct));
|
||||
await tx.CommitAsync(ct);
|
||||
await Send.OkAsync(response,ct);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user