From 90081b5335dbb4eec1157196259adc2898f5f739 Mon Sep 17 00:00:00 2001 From: Matt Bonnell Date: Wed, 24 Mar 2021 09:37:19 -0400 Subject: [PATCH] define ApplyJSONPatch helper, add tests --- client/handler.go | 20 +-------- x/json.go | 29 +++++++++++++ x/json_test.go | 102 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 19 deletions(-) create mode 100644 x/json.go create mode 100644 x/json_test.go diff --git a/client/handler.go b/client/handler.go index 4a66674bcbc..2798070c819 100644 --- a/client/handler.go +++ b/client/handler.go @@ -27,8 +27,6 @@ import ( "net/http" "time" - jsonpatch "github.com/evanphx/json-patch" - "github.com/ory/x/errorsx" "github.com/ory/herodot" @@ -186,12 +184,6 @@ func (h *Handler) Patch(w http.ResponseWriter, r *http.Request, ps httprouter.Pa return } - patch, err := jsonpatch.DecodePatch(patchJSON) - if err != nil { - h.r.Writer().WriteError(w, r, errorsx.WithStack(err)) - return - } - id := ps.ByName("id") c, err := h.r.ClientManager().GetConcreteClient(r.Context(), id) if err != nil { @@ -199,17 +191,7 @@ func (h *Handler) Patch(w http.ResponseWriter, r *http.Request, ps httprouter.Pa return } - original, err := json.Marshal(c) - if err != nil { - h.r.Writer().WriteError(w, r, err) - return - } - modified, err := patch.Apply(original) - if err != nil { - h.r.Writer().WriteError(w, r, err) - return - } - if err := json.Unmarshal(modified, c); err != nil { + if err := x.ApplyJSONPatch(patchJSON, c); err != nil { h.r.Writer().WriteError(w, r, err) return } diff --git a/x/json.go b/x/json.go new file mode 100644 index 00000000000..3d0c990aaf1 --- /dev/null +++ b/x/json.go @@ -0,0 +1,29 @@ +package x + +import ( + "encoding/json" + + jsonpatch "github.com/evanphx/json-patch" +) + +func ApplyJSONPatch(p json.RawMessage, object interface{}) error { + patch, err := jsonpatch.DecodePatch(p) + if err != nil { + return err + } + + original, err := json.Marshal(object) + if err != nil { + return err + } + + modified, err := patch.Apply(original) + if err != nil { + return err + } + + if err := json.Unmarshal(modified, object); err != nil { + return err + } + return nil +} diff --git a/x/json_test.go b/x/json_test.go new file mode 100644 index 00000000000..7421c0025ed --- /dev/null +++ b/x/json_test.go @@ -0,0 +1,102 @@ +package x + +import ( + "testing" + + "github.com/mohae/deepcopy" + "github.com/stretchr/testify/require" +) + +type TestType struct { + Field1 string + Field2 []string + Field3 struct { + Field1 bool + Field2 []int + } +} + +func TestApplyJSONPatch(t *testing.T) { + object := TestType{ + Field1: "foo", + Field2: []string{ + "foo", + "bar", + "baz", + "kaz", + }, + Field3: struct { + Field1 bool + Field2 []int + }{ + Field1: true, + Field2: []int{ + 1, + 2, + 3, + }, + }, + } + t.Run("case=empty patch", func(t *testing.T) { + rawPatch := []byte(`[]`) + obj := deepcopy.Copy(object).(TestType) + require.NoError(t, ApplyJSONPatch(rawPatch, &obj)) + require.Equal(t, object, obj) + }) + t.Run("case=field replace", func(t *testing.T) { + rawPatch := []byte(`[{"op": "replace", "path": "/Field1", "value": "boo"}]`) + expected := deepcopy.Copy(object).(TestType) + expected.Field1 = "boo" + obj := deepcopy.Copy(object).(TestType) + require.NoError(t, ApplyJSONPatch(rawPatch, &obj)) + require.Equal(t, expected, obj) + }) + t.Run("case=array replace", func(t *testing.T) { + rawPatch := []byte(`[{"op": "replace", "path": "/Field2/0", "value": "boo"}]`) + expected := deepcopy.Copy(object).(TestType) + expected.Field2[0] = "boo" + obj := deepcopy.Copy(object).(TestType) + require.NoError(t, ApplyJSONPatch(rawPatch, &obj)) + require.Equal(t, expected, obj) + }) + t.Run("case=array append", func(t *testing.T) { + rawPatch := []byte(`[{"op": "add", "path": "/Field2/-", "value": "boo"}]`) + expected := deepcopy.Copy(object).(TestType) + expected.Field2 = append(expected.Field2, "boo") + obj := deepcopy.Copy(object).(TestType) + require.NoError(t, ApplyJSONPatch(rawPatch, &obj)) + require.Equal(t, expected, obj) + }) + t.Run("case=array remove", func(t *testing.T) { + rawPatch := []byte(`[{"op": "remove", "path": "/Field2/0"}]`) + expected := deepcopy.Copy(object).(TestType) + expected.Field2 = expected.Field2[1:] + obj := deepcopy.Copy(object).(TestType) + require.NoError(t, ApplyJSONPatch(rawPatch, &obj)) + require.Equal(t, expected, obj) + }) + t.Run("case=nested field replace", func(t *testing.T) { + rawPatch := []byte(`[{"op": "replace", "path": "/Field3/Field1", "value": false}]`) + expected := deepcopy.Copy(object).(TestType) + expected.Field3.Field1 = false + obj := deepcopy.Copy(object).(TestType) + require.NoError(t, ApplyJSONPatch(rawPatch, &obj)) + require.Equal(t, expected, obj) + }) + t.Run("case=nested array append", func(t *testing.T) { + rawPatch := []byte(`[{"op": "add", "path": "/Field3/Field2/-", "value": 4}]`) + expected := deepcopy.Copy(object).(TestType) + expected.Field3.Field2 = append(expected.Field3.Field2, 4) + obj := deepcopy.Copy(object).(TestType) + require.NoError(t, ApplyJSONPatch(rawPatch, &obj)) + require.Equal(t, expected, obj) + }) + t.Run("case=nested array remove", func(t *testing.T) { + rawPatch := []byte(`[{"op": "remove", "path": "/Field3/Field2/2"}]`) + expected := deepcopy.Copy(object).(TestType) + expected.Field3.Field2 = expected.Field3.Field2[:2] + obj := deepcopy.Copy(object).(TestType) + require.NoError(t, ApplyJSONPatch(rawPatch, &obj)) + require.Equal(t, expected, obj) + }) +}