internal/infra/pg/subjectstore.go
internal/infra/pg · 266 lines · 21 declarations · source
Declarations
var ErrInvalidSubject, ErrSubjectNotFound, ErrSubjectConflict
var (
ErrInvalidSubject = errors.New("invalid subject request")
ErrSubjectNotFound = errors.New("subject not found")
ErrSubjectConflict = errors.New("subject reference, retry or version conflict")
)
type SubjectStore
type SubjectStore struct {
pool *pgxpool.Pool
schema Schema
}
SubjectStore holds optional identity bookkeeping. It never changes a source's stored attribution and its external references never participate in inference, displayed claims or recall.
func NewSubjectStore
func NewSubjectStore(pool *pgxpool.Pool, schema Schema) *SubjectStore
type SubjectFields
type SubjectFields struct {
ExternalReference string `json:"external_reference,omitempty"`
Label string `json:"label"`
}
type SubjectRegistration
type SubjectRegistration struct {
IdempotencyKey string `json:"idempotency_key"`
SubjectFields
}
type SubjectUpdate
type SubjectUpdate struct {
ID string `json:"id"`
ExpectedVersion string `json:"expected_version"`
SubjectFields
}
type ManagedSubject
type ManagedSubject struct {
ID string `json:"id"`
Version string `json:"version"`
SubjectFields
UpdatedBy string `json:"updated_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
InactiveAfter *time.Time `json:"inactive_after"`
}
type SubjectWrite
type SubjectWrite struct {
ManagedSubject
Replayed bool `json:"replayed"`
}
type SubjectPage
type SubjectPage struct {
Subjects []ManagedSubject `json:"subjects"`
Next *RecordCursor `json:"next,omitempty"`
}
func subjectUUID
func subjectUUID(value string) (string, error)
func subjectFieldsValid
func subjectFieldsValid(f SubjectFields) bool
func subjectDigest
func subjectDigest(f SubjectFields) []byte
func subjectFailure
func subjectFailure(err error) error
const subjectColumns
const subjectColumns = `s.subject_id,s.version::text,coalesce(s.external_reference,''),s.label,s.updated_by::text,s.created_at,s.updated_at,s.inactive_after`
const subjectLive
const subjectLive = `(s.inactive_after IS NULL OR s.inactive_after>now() OR EXISTS(SELECT 1 FROM {schema}.observation o WHERE o.scope=s.scope AND o.data_subject_id=s.subject_id))`
func subjectDest
func subjectDest(s *ManagedSubject) []any
method SubjectStore.Register
func (s *SubjectStore) Register(ctx context.Context, scope, principal string, r SubjectRegistration) (SubjectWrite, error)
Register uses a random ID rather than embedding or hashing a personal reference into attribution. A request's original fingerprint supports lost acknowledgements; erasure leaves only a retry-key tombstone so a delayed registration cannot recreate an erased mapping.
method SubjectStore.Get
func (s *SubjectStore) Get(ctx context.Context, scope, id, reference string) (ManagedSubject, error)
Get resolves exactly one project-scoped ID or external reference. No normalization guesses at an application's identifier semantics, and an expired/foreign/absent entry is the same refusal.
method SubjectStore.Update
func (s *SubjectStore) Update(ctx context.Context, scope, principal string, r SubjectUpdate) (SubjectWrite, error)
Update rotates a reference without rewriting attribution. The immediate previous version can retry an identical update; a different stale request cannot overwrite a concurrent accepted one.
method SubjectStore.List
func (s *SubjectStore) List(ctx context.Context, scope, after string, limit int) (SubjectPage, error)
func auditSubjectMutation
func auditSubjectMutation(ctx context.Context, tx pgx.Tx, schema Schema, scope, principal, operation string, magnitude int) error