Files
go-sip/internal/store/local_v01_verify.go
T

67 lines
2.4 KiB
Go

package store
import (
"database/sql"
"encoding/json"
"errors"
)
// VerifyLocalAcceptedCommand binds a final call result to the exact accepted
// command and execution recorded in the durable inbox and receipt outbox.
func (s *Store) VerifyLocalAcceptedCommand(tenantID, tenantKey, commandID, executionID, taskID string) error {
if tenantID == "" || tenantKey == "" || commandID == "" || executionID == "" || taskID == "" {
return ErrCommandConflict
}
s.mu.Lock()
defer s.mu.Unlock()
var storedTenantKey, status string
var commandBody, receiptBody []byte
err := s.db.QueryRow(`SELECT i.tenant_key, i.status, i.body, o.body
FROM inbox i
JOIN mq_command_receipts r ON r.tenant_id=i.tenant_id AND r.command_id=i.command_id
JOIN outbox o ON o.event_id=r.response_id
WHERE i.tenant_id=? AND i.command_id=?`, tenantID, commandID).Scan(&storedTenantKey, &status, &commandBody, &receiptBody)
if errors.Is(err, sql.ErrNoRows) {
return ErrCommandConflict
}
if err != nil {
return err
}
if storedTenantKey != tenantKey || status != "persisted" {
return ErrCommandConflict
}
var command struct {
CommandID string `json:"command_id"`
CommandType string `json:"command_type"`
TenantID string `json:"tenant_id"`
TenantKey string `json:"tenant_key"`
Payload struct {
TaskID string `json:"task_id"`
} `json:"payload"`
}
if err := json.Unmarshal(commandBody, &command); err != nil {
return ErrCommandConflict
}
var receipt struct {
EventType string `json:"event_type"`
TenantID string `json:"tenant_id"`
TenantKey string `json:"tenant_key"`
Payload struct {
CommandID string `json:"command_id"`
CommandType string `json:"command_type"`
Status string `json:"status"`
ExecutionID string `json:"execution_id"`
} `json:"payload"`
}
if err := json.Unmarshal(receiptBody, &receipt); err != nil {
return ErrCommandConflict
}
if command.CommandID != commandID || command.CommandType != "call.execute" || command.TenantID != tenantID ||
command.TenantKey != tenantKey || command.Payload.TaskID != taskID || receipt.EventType != "command.result" ||
receipt.TenantID != tenantID || receipt.TenantKey != tenantKey || receipt.Payload.CommandID != commandID ||
receipt.Payload.CommandType != "call.execute" || receipt.Payload.Status != "accepted" || receipt.Payload.ExecutionID != executionID {
return ErrCommandConflict
}
return nil
}