a73x

db525127

feat: add middleware package with YAML config loading

a73x   2026-03-31 05:46

Commit message
feat: add middleware package with YAML config loading

middleware/middleware.go
Old New
@@ -0,0 +1,59 @@
1 package middleware
2
3 import (
4 "fmt"
5 "os"
6
7 "gopkg.in/yaml.v3"
8 )
9
10 type Rule struct {
11 Match string
12 Action string
13 Dest string
14 }
15
16 type Middleware struct {
17 rules []Rule
18 }
19
20 type yamlRule struct {
21 Match string `yaml:"match"`
22 Action string `yaml:"action"`
23 Dest string `yaml:"dest"`
24 }
25
26 type yamlConfig struct {
27 Middleware []yamlRule `yaml:"middleware"`
28 }
29
30 // New loads middleware config from a YAML file.
31 // Returns an empty Middleware if the file does not exist.
32 func New(path string) (*Middleware, error) {
33 data, err := os.ReadFile(path)
34 if err != nil {
35 if os.IsNotExist(err) {
36 return &Middleware{}, nil
37 }
38 return nil, fmt.Errorf("reading middleware config: %w", err)
39 }
40
41 var cfg yamlConfig
42 if err := yaml.Unmarshal(data, &cfg); err != nil {
43 return nil, fmt.Errorf("parsing middleware YAML: %w", err)
44 }
45
46 rules := make([]Rule, 0, len(cfg.Middleware))
47 for _, yr := range cfg.Middleware {
48 if yr.Action != "save_response" {
49 return nil, fmt.Errorf("unknown middleware action %q for match %q", yr.Action, yr.Match)
50 }
51 rules = append(rules, Rule{Match: yr.Match, Action: yr.Action, Dest: yr.Dest})
52 }
53
54 return &Middleware{rules: rules}, nil
55 }
56
57 func (m *Middleware) RuleCount() int {
58 return len(m.rules)
59 }
middleware/middleware_test.go
Old New
@@ -0,0 +1,52 @@
1 package middleware_test
2
3 import (
4 "os"
5 "path/filepath"
6 "testing"
7
8 "github.com/xanderle/nono/middleware"
9 )
10
11 func TestNewLoadsConfig(t *testing.T) {
12 dir := t.TempDir()
13 path := filepath.Join(dir, "middleware.yaml")
14 os.WriteFile(path, []byte(`middleware:
15 - match: "api.anthropic.com/api/oauth/usage"
16 action: save_response
17 dest: "/tmp/usage.json"
18 `), 0644)
19
20 mw, err := middleware.New(path)
21 if err != nil {
22 t.Fatalf("unexpected error: %v", err)
23 }
24 if mw.RuleCount() != 1 {
25 t.Errorf("expected 1 rule, got %d", mw.RuleCount())
26 }
27 }
28
29 func TestNewReturnsEmptyForMissingFile(t *testing.T) {
30 mw, err := middleware.New("/nonexistent/middleware.yaml")
31 if err != nil {
32 t.Fatalf("unexpected error: %v", err)
33 }
34 if mw.RuleCount() != 0 {
35 t.Errorf("expected 0 rules, got %d", mw.RuleCount())
36 }
37 }
38
39 func TestNewRejectsUnknownAction(t *testing.T) {
40 dir := t.TempDir()
41 path := filepath.Join(dir, "middleware.yaml")
42 os.WriteFile(path, []byte(`middleware:
43 - match: "example.com/foo"
44 action: delete_everything
45 dest: "/tmp/out"
46 `), 0644)
47
48 _, err := middleware.New(path)
49 if err == nil {
50 t.Fatal("expected error for unknown action")
51 }
52 }