package lua // Per-caller state scoping. // // Role visibility answers "may this role see the plugin at all". It does not // answer "which PART of its data", and for the billing plugin those are // different questions: the global figures (total spend, every source, every // model) are deployment data, while the per-key buckets describe one caller. // // So a plugin may declare a split, and the gateway then hands a non-admin // caller only the buckets that are theirs. The plugin declares it because only // the plugin author knows which of its buckets are global. import "strings" // StateScope is a plugin's declaration of which state buckets are global. type StateScope struct { // Global names the buckets that describe the whole deployment. Anything not // listed is treated as per-caller and filtered. Global []string `json:"global,omitempty"` // Depth gives the nesting level of each PER-CALLER bucket: 1 for // {maskedKey: bucket}, 2 for {day: {maskedKey: bucket}}. It is declared // because both shapes are maps and sniffing would confuse an empty bucket // with a nested one. Depth map[string]int `json:"depth,omitempty"` } // IsGlobal reports whether a top-level state key is deployment-wide. func (s *StateScope) IsGlobal(key string) bool { if s == nil { return true } for _, g := range s.Global { if g == key { return true } } return false } // StateScopeFor returns a plugin's declared split, or nil when it declares none. func (ps *Plugins) StateScopeFor(name string) *StateScope { ps.mu.RLock() defer ps.mu.RUnlock() for _, p := range ps.plugins { if p.Info.Name != name || p.UI == nil { continue } return p.UI.StateScope } return nil } // compileDepthMap normalizes the declared depth table, defaulting to 1 for a // per-caller bucket the author did not annotate. func compileDepthMap(in map[string]int) map[string]int { if len(in) == 0 { return nil } out := make(map[string]int, len(in)) for k, v := range in { if v < 1 { v = 1 } out[strings.TrimSpace(k)] = v } return out } // DepthOf returns the declared nesting level for a per-caller bucket, 1 by // default. A bucket the author did not annotate is assumed flat, which is the // common shape ({maskedKey: row}). func (s *StateScope) DepthOf(key string) int { if s == nil || s.Depth == nil { return 1 } if d, ok := s.Depth[key]; ok { return d } return 1 }