@@ -11,6 +11,7 @@ import (
1111
1212 jsonpatch "github.com/evanphx/json-patch/v5"
1313 "github.com/gobwas/glob"
14+ "github.com/pkg/errors"
1415
1516 "github.com/ory/x/pointerx"
1617)
@@ -43,33 +44,34 @@ func isElementAccess(path string) bool {
4344 return false
4445}
4546
46- // ApplyJSONPatch applies a JSON patch to an object. It returns an error if the
47- // patch is invalid or if the patch includes paths that are denied. denyPaths is
48- // a list of path globs (interpreted with [glob.Compile] that are not allowed to
47+ // ApplyJSONPatch applies a JSON patch to an object and returns the modified
48+ // object. The original object is not modified. It returns an error if the patch
49+ // is invalid or if the patch includes paths that are denied. denyPaths is a
50+ // list of path globs (interpreted with [glob.Compile] that are not allowed to
4951// be patched.
50- func ApplyJSONPatch (p json.RawMessage , object interface {} , denyPaths ... string ) error {
52+ func ApplyJSONPatch [ T any ] (p json.RawMessage , object T , denyPaths ... string ) ( result T , err error ) {
5153 patch , err := jsonpatch .DecodePatch (p )
5254 if err != nil {
53- return err
55+ return result , errors . WithStack ( err )
5456 }
5557
5658 denyPattern := fmt .Sprintf ("{%s}" , strings .ToLower (strings .Join (denyPaths , "," )))
5759 matcher , err := glob .Compile (denyPattern , '/' )
5860 if err != nil {
59- return err
61+ return result , errors . WithStack ( err )
6062 }
6163
6264 for _ , op := range patch {
6365 // Some operations are buggy, see https://github.com/evanphx/json-patch/pull/158
6466 if isUnsupported (op ) {
65- return fmt .Errorf ("unsupported operation: %s" , op .Kind ())
67+ return result , errors .Errorf ("unsupported operation: %s" , op .Kind ())
6668 }
6769 path , err := op .Path ()
6870 if err != nil {
69- return fmt .Errorf ("error parsing patch operations: %v" , err )
71+ return result , errors .Errorf ("error parsing patch operations: %v" , err )
7072 }
7173 if matcher .Match (strings .ToLower (path )) {
72- return fmt .Errorf ("patch includes denied path: %s" , path )
74+ return result , errors .Errorf ("patch includes denied path: %s" , path )
7375 }
7476
7577 // JSON patch officially rejects replacing paths that don't exist, but we want to be more tolerant.
@@ -81,16 +83,17 @@ func ApplyJSONPatch(p json.RawMessage, object interface{}, denyPaths ...string)
8183
8284 original , err := json .Marshal (object )
8385 if err != nil {
84- return err
86+ return result , errors . WithStack ( err )
8587 }
8688
8789 options := jsonpatch .NewApplyOptions ()
8890 options .EnsurePathExistsOnAdd = true
8991
9092 modified , err := patch .ApplyWithOptions (original , options )
9193 if err != nil {
92- return err
94+ return result , errors . WithStack ( err )
9395 }
9496
95- return json .Unmarshal (modified , object )
97+ err = json .Unmarshal (modified , & result )
98+ return result , errors .WithStack (err )
9699}
0 commit comments