Documentation
¶
Overview ¶
Package internal is every implementation of the audit module. Nothing outside modules/audit can import it, which is the compiler enforcing idea 3.
Index ¶
- func RegisterRoutes(api *httpx.API, svc contracts.Service)
- func Retention(tenants jobs.TenantLister, days int) jobs.Job
- type Service
- func (s *Service) Get(_ context.Context, tx db.Tx[db.Tenant], id uuid.UUID) (*contracts.Event, error)
- func (s *Service) List(_ context.Context, tx db.Tx[db.Tenant], q contracts.Query) ([]*contracts.Event, int64, error)
- func (s *Service) Record(_ context.Context, tx db.Tx[db.Tenant], ev events.Event) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RegisterRoutes ¶
RegisterRoutes mounts the two routes an append-only trail has. They are written by hand rather than mounted from a rest.Spec, and that is the shape of this module rather than an omission: a Spec is five routes and three of them write. A trail nobody can create, update or delete a row in is not a resource with three operations turned off — it is a different thing, and saying so here is cheaper than a Spec with three holes in it. Both answer through kit/rest's mapping, so a 404 means what it means everywhere.
func Retention ¶
func Retention(tenants jobs.TenantLister, days int) jobs.Job
Retention is the module's periodic work: the trail forgets what it is no longer obliged to keep. It is a job and not a subscription because nothing happens when a row expires — the clock passes, which is the distinction docs/adr/0004 draws. It publishes nothing, like everything else here.
Types ¶
type Service ¶
type Service struct{}
Service is the audit trail. It has no fields: everything a command needs arrives with the transaction, so one instance serves a request and an event handler at once.
func (*Service) Get ¶
func (s *Service) Get(_ context.Context, tx db.Tx[db.Tenant], id uuid.UUID) (*contracts.Event, error)
Get is one row of this tenant's trail. A row another tenant owns is not found, which is the only thing the API may say about it.
func (*Service) List ¶
func (s *Service) List(_ context.Context, tx db.Tx[db.Tenant], q contracts.Query) ([]*contracts.Event, int64, error)
List is a page of this tenant's trail, newest first. It is a hand-written query and not a crud.List because two of the filters are range comparisons, which kit/crud's equalities cannot express — the same reason the SLA sweep writes its own. The count and the page come from separate statements, because GORM carries clauses forward and a Count that inherited the LIMIT would count the page; the order ends in the id, so two rows recorded in one transaction neither overlap nor skip between pages.
func (*Service) Record ¶
Record writes one event into the trail. The insert is by hand because the row is not a crud.Entity — nothing here is updated or soft-deleted — and because the conflict clause is the point: recording is idempotent by the tenant and the event's own id, the second lock behind the kernel's handled table. That one claims each delivery of each subscription; this one covers what it cannot, which is an operator replaying an outbox row or a handler that failed after writing. The tenant is part of the key because the thing being made idempotent is a row in one tenant's trail — see migrations/000015.
The tenant comes from the transaction and not from ev.TenantID. The two always agree, because Consume opened this transaction in the event's own tenant; taking it from the transaction anyway is what makes the isolation the database's rather than a struct field's.