Repository navigation
feat(indústria): tornar apontamento parcial idempotente e rastreável - #416
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e912021a66
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (string.IsNullOrWhiteSpace(key) || key.Length > 160) return BadRequest(ApiResponse<object>.Fail("Idempotency-Key válida é obrigatória.", cid)); | ||
| var payloadHash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(r)))).ToLowerInvariant(); | ||
| var tenantId = RequireTenant(); using var c = _context.CreateConnection(); c.Open(); using var tx = c.BeginTransaction(System.Data.IsolationLevel.ReadCommitted); | ||
| var repetido = await c.QuerySingleOrDefaultAsync<(long Id, string PayloadHash)?>("select id, payload_hash as PayloadHash from sigov.industria_apontamento where tenant_id=@TenantId and idempotency_key=@Key for update", new { TenantId = tenantId, Key = key }, tx); |
There was a problem hiding this comment.
Claim the idempotency key atomically
When two requests with the same tenant and key arrive before either insert commits, both queries can observe no row because FOR UPDATE cannot lock a nonexistent record. After they serialize on the production-order lock, the second request reaches the insert and violates ux_industria_apontamento_idempotencia, which the generic catch converts to HTTP 500 instead of an idempotent replay or payload conflict. Claim the key atomically, such as with an upsert or a key-scoped advisory lock, before deciding whether this is a replay.
Useful? React with 👍 / 👎.
| if (r.QuantidadeBoas < 0 || r.QuantidadeRefugo < 0 || (r.Tipo == "PRODUCAO" && r.QuantidadeBoas + r.QuantidadeRefugo <= 0)) return BadRequest(ApiResponse<object>.Fail("A produção informada deve ser positiva.", cid)); | ||
| var key = Request.Headers["Idempotency-Key"].ToString().Trim(); | ||
| if (string.IsNullOrWhiteSpace(key) || key.Length > 160) return BadRequest(ApiResponse<object>.Fail("Idempotency-Key válida é obrigatória.", cid)); | ||
| var payloadHash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(r)))).ToLowerInvariant(); |
There was a problem hiding this comment.
Include the target order in replay equivalence
The payload hash serializes only the body and omits the route's production-order id. If a key previously used for order A is sent to order B with the same body, the tenant-wide lookup treats it as an identical replay and returns A's appointment ID with HTTP 200 while making no change to B. Include the order ID in the hash or fetch and compare the stored ordem_id, returning a conflict when the key targets another order.
Useful? React with 👍 / 👎.
Motivation
Description
IndustriaController.ApontarAsyncfoi reforçada para exigirIdempotency-Key, calcular um SHA-256 do payload, checar/reusar a entrada idempotente ou rejeitar chave reutilizada com conteúdo diferente, bloquear a OP comFOR UPDATE, validar estado e limitar produção acumulada dentro do planejado, e executar inserção do apontamento + atualização da OP + auditoria em uma única transação.database/postgres/migrations/20260915120000_industria_fluxo_operacional.sqlque introduz colunas de versionamento e controle (version,quantidade_aprovada,quantidade_rejeitada), campos de idempotência e rastreabilidade em apontamento/consumo/produção/inspeção, índices únicos de idempotência e constraints de integridade de quantidades.script_completo*foram atualizados com a nova migration, e os testes existentes foram ampliados para verificar contratos estáticos relativos à idempotência,for update, prevenção de dupla contabilização e presença da migration.Testing
python3 -m json.tool database/postgres/migrations/manifest.jsonpassed and migration checksum verified against the manifest,./scripts/check-api-route-conflicts.shfound no route conflicts,bash -n scripts/*.sh scripts/ci/*.shsucceeded, andgit diff --checkand conflict-marker scan returned clean results.manifest.jsonand to consolidated scripts (database/postgres/script_completo.sql,script_completop.sql, etc.), and index/partial-index checks ran producing non-blocking warnings (repository-wide historic warnings) but no blocking failures.dotnet restore/dotnet build/ unit/integration tests and Razor/Swagger validation were not executed because the environment lacks the .NET SDK,psql/PostgreSQL andpwsh(these checks are marked blocked and must be run in CI or a local runtime with PostgreSQL 16 and .NET 10).Codex Task