From c663b1a12c666b7503b8c23afb74e008303ef581 Mon Sep 17 00:00:00 2001 From: bel Date: Sun, 26 Mar 2023 14:17:33 -0600 Subject: [PATCH] expose PATCH /config --- src/device/input/parse/v01/config.go | 27 +++++---- src/device/input/parse/v01/config_test.go | 2 +- src/device/input/parse/v01/server.go | 21 +++++++ src/device/input/parse/v01/server_test.go | 74 +++++++++++++++++++++++ 4 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 src/device/input/parse/v01/server_test.go diff --git a/src/device/input/parse/v01/config.go b/src/device/input/parse/v01/config.go index 0f983cf..178b1d9 100644 --- a/src/device/input/parse/v01/config.go +++ b/src/device/input/parse/v01/config.go @@ -6,27 +6,32 @@ import ( patch "github.com/evanphx/json-patch/v5" ) -type config struct { - Feedback struct { +type ( + config struct { + Feedback configFeedback + Users map[string]configUser + Players []configPlayer + Quiet bool + } + + configFeedback struct { Addr string TTSURL string } - Users map[string]struct { + + configUser struct { Player int Message string } - Players []struct { + + configPlayer struct { Transformation transformation } - Quiet bool -} +) -func (cfg config) WithJSONPatch(v interface{}) config { +func (cfg config) WithPatch(v interface{}) config { originalData, _ := json.Marshal(cfg) - patchData, err := json.Marshal(v) - if err != nil { - return cfg - } + patchData, _ := json.Marshal(v) patcher, err := patch.DecodePatch(patchData) if err != nil { return cfg diff --git a/src/device/input/parse/v01/config_test.go b/src/device/input/parse/v01/config_test.go index 365e44b..859fdf9 100644 --- a/src/device/input/parse/v01/config_test.go +++ b/src/device/input/parse/v01/config_test.go @@ -70,7 +70,7 @@ func TestConfigPatch(t *testing.T) { for name, d := range cases { c := d t.Run(name, func(t *testing.T) { - got := c.cfg.WithJSONPatch(c.patch) + got := c.cfg.WithPatch(c.patch) if fmt.Sprintf("%+v", got) != fmt.Sprintf("%+v", c.want) { t.Errorf("(%+v).Patch(%+v) want %+v, got %+v", c.cfg, c.patch, c.want, got) } diff --git a/src/device/input/parse/v01/server.go b/src/device/input/parse/v01/server.go index 5ddd722..241089d 100644 --- a/src/device/input/parse/v01/server.go +++ b/src/device/input/parse/v01/server.go @@ -1,12 +1,16 @@ package v01 import ( + "encoding/json" "io" "log" "mayhem-party/src/device/input/wrap" "net/http" + "os" "sync" "syscall" + + "gopkg.in/yaml.v2" ) func (v01 *V01) listen() { @@ -55,6 +59,8 @@ func (v01 *V01) serveHTTP(w http.ResponseWriter, r *http.Request) { v01.getUserFeedback(w, r) case "/broadcast": v01.putBroadcast(w, r) + case "/config": + v01.patchConfig(w, r) } } @@ -73,6 +79,21 @@ func (v01 *V01) putBroadcast(w http.ResponseWriter, r *http.Request) { v01.cfg.Users["broadcast"] = v } +func (v01 *V01) patchConfig(w http.ResponseWriter, r *http.Request) { + b, _ := io.ReadAll(r.Body) + var v []interface{} + if err := json.Unmarshal(b, &v); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + v01.cfg = v01.cfg.WithPatch(v) + if b, err := yaml.Marshal(v01.cfg); err == nil && FlagParseV01Config != "" { + if err := os.WriteFile(FlagParseV01Config, b, os.ModePerm); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + } +} + func (v01 *V01) globalQueries(r *http.Request) { v01.globalQuerySay(r) v01.globalQueryRefresh(r) diff --git a/src/device/input/parse/v01/server_test.go b/src/device/input/parse/v01/server_test.go new file mode 100644 index 0000000..25178ef --- /dev/null +++ b/src/device/input/parse/v01/server_test.go @@ -0,0 +1,74 @@ +package v01 + +import ( + "fmt" + "net/http" + "net/http/httptest" + "os" + "path" + "strings" + "testing" + + "gopkg.in/yaml.v2" +) + +func TestPatchConfig(t *testing.T) { + dir := t.TempDir() + p := path.Join(dir, t.Name()+".yaml") + cases := map[string]struct { + was config + patch string + want config + }{ + "replace entire doc": { + was: config{ + Feedback: configFeedback{Addr: "a", TTSURL: "a"}, + Users: map[string]configUser{"a": configUser{Player: 1, Message: "a"}}, + Players: []configPlayer{configPlayer{Transformation: transformation{"a": "a"}}}, + Quiet: true, + }, + patch: `[{"op": "replace", "path": "", "value": { + "Feedback": {"Addr": "b", "TTSURL": "b"}, + "Users": {"b": {"Player": 2, "Message": "b"}}, + "Players": [{"Transformation": {"b": "b"}}], + "Quiet": false + }}]`, + want: config{ + Feedback: configFeedback{Addr: "b", TTSURL: "b"}, + Users: map[string]configUser{"b": configUser{Player: 2, Message: "b"}}, + Players: []configPlayer{configPlayer{Transformation: transformation{"b": "b"}}}, + Quiet: false, + }, + }, + } + + for name, d := range cases { + c := d + for _, usesdisk := range []bool{false, true} { + t.Run(fmt.Sprintf("%s disk=%v", name, usesdisk), func(t *testing.T) { + b, _ := yaml.Marshal(c.was) + os.WriteFile(p, b, os.ModePerm) + FlagParseV01Config = "" + if usesdisk { + FlagParseV01Config = p + } + v01 := &V01{cfg: c.was} + + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPatch, "/config", strings.NewReader(c.patch)) + v01.patchConfig(w, r) + if fmt.Sprintf("%+v", c.want) != fmt.Sprintf("%+v", v01.cfg) { + t.Errorf("want \n\t%+v, got \n\t%+v", c.want, v01.cfg) + } + if usesdisk { + b, _ := os.ReadFile(p) + var got config + yaml.Unmarshal(b, &got) + if fmt.Sprintf("%+v", c.want) != fmt.Sprintf("%+v", v01.cfg) { + t.Errorf("want \n\t%+v, got \n\t%+v", c.want, v01.cfg) + } + } + }) + } + } +}