Files
mesh-catalog/modules/distribution/internal/holder/findings.go
T

81 lines
3.0 KiB
Go

package holder
import (
"context"
"strings"
"time"
meshconditions "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions"
keyparts "git.novox.be/novox/mesh-sdk/go/keys"
"git.novox.be/novox/mesh-sdk/go/views"
)
// FindingKinds are the kinds the driver says (the program) and the transfers say (the tools bundle): each bundle's writer
// owns its own, since the two share one state `findings` (design 47 G §580, §689).
var (
DriverFindingKinds = []string{FindingWindowHeld}
TransferFindingKinds = []string{"publish-refused", "incoming-full"}
)
// FindingWriter is the module's writer (views.Writer) as the keeper writes and clears through it.
type FindingWriter interface {
Write(ctx context.Context, finding meshconditions.Finding) error
Clear(ctx context.Context, scope, id, kind, why string) error
}
// Findings keeps a bundle's open findings and says them through the module's writer (design 47 G §689, A-14): its `Say` is
// the Finding callback of the Driver and the Holder; the writer is the held writer (views.HeldWriter), which holds what is open (A-29, A-32).
type Findings struct {
Writer FindingWriter
Logf func(string, ...any)
Now func() time.Time
}
// Say raises (raised) or clears the finding of a kind about a subject. Its id is the seat's and the subject's, each escaped
// as a key's name is (design 47 G A-14); the kind stays the bundle's own word.
func (k *Findings) Say(kind, subject, words string, raised bool) {
id := keyparts.EscapeName(Self) + "." + keyparts.EscapeName(subject)
key := views.FindingKey("seat", id, kind)
if !raised {
if err := k.Writer.Clear(context.Background(), "seat", id, kind, "it no longer holds"); err != nil {
k.Logf("the finding %s could not be cleared: %v", key, err)
}
return
}
at := time.Now()
if k.Now != nil {
at = k.Now()
}
f := meshconditions.Finding{Scope: "seat", ID: id, Kind: kind, Subject: meshconditions.Subject{Scope: "seat", ID: id},
Severity: meshconditions.FindingSeverityWarning, Summary: words, Headline: words, Explanation: words,
Source: kind, Observer: Self, At: at.UTC()}
if err := k.Writer.Write(context.Background(), f); err != nil {
k.Logf("the finding %s could not be written: %v", key, err)
}
}
// DeleteOldFindings deletes, once at a module's start, the findings the keys of an older form hold (design 47 G A-31): the form
// `seat.<seat>.<kind>_<subject>`, whose kind is no kind the writer owns in its key, so the writer's own first statement does not
// clear it. Only a key of one of kinds is deleted; the keys of the writer's form (`seat.<seat>.<subject>.<kind>`) are left.
func DeleteOldFindings(state views.FindingState, seat string, kinds []string) error {
keys, err := state.Keys()
if err != nil {
return err
}
for _, k := range keys {
rest, ok := strings.CutPrefix(k, "seat."+seat+".")
if !ok {
continue
}
for _, kind := range kinds {
if strings.HasPrefix(rest, kind+"_") {
if err := state.Delete(k); err != nil {
return err
}
break
}
}
}
return nil
}