Files
mesh-contracts/go/clients/meshconfigclient/client.go
T

84 lines
3.4 KiB
Go

// Package meshconfigclient is the logic every reader of the seat mesh-config's state kept shares (novox/hq ADR 0565
// clause 5, ADR 0634 §4): the checker and the assigner's composition check lay a pull request's own held values over its
// modules' values. The layout of a value is the seat's contract's; this holds what the readers share over it: a pull
// request's key, a held value's key and the rule that a value is the one its key names.
package meshconfigclient
import (
"encoding/json"
"fmt"
"strings"
seat "git.novox.be/novox/mesh-contracts/go/seats/meshconfig"
)
// PullKey is a pull request's key, `owner/repository#number` with `/` and `#` as dots, the form the seat keeps a pull
// request's values under. The three parts hold no dot, star, angle bracket or space.
func PullKey(pull string) (string, error) {
owner, repository, number, err := splitPull(pull)
if err != nil {
return "", err
}
return owner + "." + repository + "." + number, nil
}
func splitPull(pull string) (owner, repository, number string, err error) {
owner, rest, ok := strings.Cut(strings.TrimSpace(pull), "/")
repository, number, ok2 := strings.Cut(rest, "#")
if !ok || !ok2 || owner == "" || repository == "" || number == "" || strings.ContainsAny(owner+repository+number, ".*> ") {
return "", "", "", fmt.Errorf("meshconfig: %q is not a pull request, owner/repository#number", pull)
}
return owner, repository, number, nil
}
// HeldValueKey is a held value's key in the state kept: the pull request's key, the node, the module and the config key,
// joined by dots.
func HeldValueKey(pull, node, module, key string) (string, error) {
owner, repository, number, err := splitPull(pull)
if err != nil {
return "", err
}
if node == "" || module == "" || key == "" {
return "", fmt.Errorf("meshconfig: a held value names a node, a module and a key")
}
return seat.KeptByOwnerRepositoryNumberNodeModuleKeyKey(owner, repository, number, node, module, key), nil
}
// Kept is one held value, as the seat types it.
type Kept = seat.KeptByOwnerRepositoryNumberNodeModuleKeyValue
// CheckKept refuses a held value that names no pull request, head, declaration, node, module or key.
func CheckKept(v Kept) error {
if _, err := PullKey(v.Pull); err != nil {
return err
}
if v.Head == "" || v.Declaration == "" || v.Node == "" || v.Module == "" || v.Key == "" {
return fmt.Errorf("meshconfig: a held value of %s names a head, a declaration, a node, a module and a key", v.Pull)
}
return nil
}
// DecodeKept reads a value of the state kept under its key: the value the seat types, checked, and the one its key
// names. A value kept under another key than its own fields make is refused, so a reader never lays a value over the
// wrong pull request or module.
func DecodeKept(key string, raw []byte) (Kept, error) {
v, err := seat.DecodeKeptByOwnerRepositoryNumberNodeModuleKeyValue(raw)
if err != nil {
return Kept{}, fmt.Errorf("meshconfig: the held value %s is not one: %w", key, err)
}
if err := CheckKept(v); err != nil {
return Kept{}, err
}
want, err := HeldValueKey(v.Pull, v.Node, v.Module, v.Key)
if err != nil {
return Kept{}, err
}
if want != key {
return Kept{}, fmt.Errorf("meshconfig: the held value kept under %s names %s", key, want)
}
return v, nil
}
// ValueOf is the value held, as the operator approved it, in its JSON form.
func ValueOf(v Kept) (json.RawMessage, error) { return json.Marshal(v.Value) }