-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
590 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.idea | ||
vendor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/denouche/go-json-patch-jsonpath | ||
|
||
go 1.18 | ||
|
||
require github.com/ohler55/ojg v1.17.2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
github.com/ohler55/ojg v1.17.1 h1:tctLinqjSLksyXOip6ALetxK4l9CRGH6K5KSYUYUrYI= | ||
github.com/ohler55/ojg v1.17.1/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= | ||
github.com/ohler55/ojg v1.17.2 h1:e8vUJa6l9v9li0T5xqQIDhVFnialk21LA1biXJlu0Rs= | ||
github.com/ohler55/ojg v1.17.2/go.mod h1:7Ghirupn8NC8hSSDpI0gcjorPxj+vSVIONDWfliHR1k= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package jsonpatch | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
const ( | ||
PatchOperationAdd = "add" | ||
PatchOperationRemove = "remove" | ||
PatchOperationReplace = "replace" | ||
) | ||
|
||
type PatchOperation string | ||
|
||
type PatchRequest[T any] struct { | ||
Operation PatchOperation `json:"op" validate:"required,oneof=remove replace"` // TODO implements add | ||
Path string `json:"path" validate:"required,jsonpath,ne=$"` | ||
Value any `json:"value"` | ||
} | ||
|
||
// Apply TODO | ||
func (pr *PatchRequest[T]) Apply(initialResource *T, emptyResource *T) (*T, error) { | ||
switch pr.Operation { | ||
case PatchOperationReplace: | ||
return pr.replace(initialResource, emptyResource) | ||
case PatchOperationRemove: | ||
return pr.remove(initialResource, emptyResource) | ||
case PatchOperationAdd: | ||
//TODO make the implementation | ||
fallthrough // fallthrough for now | ||
default: | ||
return nil, errors.New("operation not implemented") | ||
} | ||
} | ||
|
||
func (pr *PatchRequest[T]) remarshal(resourceAsMap interface{}, emptyResource *T) (*T, error) { | ||
newBytes, err := json.Marshal(resourceAsMap) | ||
if err != nil { | ||
return nil, fmt.Errorf("match fail to marshal input resource %s", err.Error()) | ||
} | ||
|
||
err = json.Unmarshal(newBytes, &emptyResource) | ||
if err != nil { | ||
return nil, fmt.Errorf("match fail to unmarshal %s", err.Error()) | ||
} | ||
|
||
return emptyResource, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package jsonpatch | ||
|
||
type PatchRequests[T any] struct { | ||
Patches []*PatchRequest[T] `json:"patches" validate:"required,min=1,dive,required"` | ||
} | ||
|
||
// Apply TODO | ||
func (prs *PatchRequests[T]) Apply(initialResource *T, newer func() *T) (*T, error) { | ||
var err error | ||
patched := initialResource | ||
for _, pr := range prs.Patches { | ||
patched, err = pr.Apply(initialResource, newer()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
return patched, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package jsonpatch | ||
|
||
type MyStruct struct { | ||
FieldString string `json:"field_string"` | ||
FieldStringPtr *string `json:"field_string_ptr"` | ||
FieldMapStringString map[string]string `json:"field_map_string_string"` | ||
FieldArrayString []string `json:"field_array_string"` | ||
FieldStruct *MySubStruct `json:"field_struct"` | ||
FieldArrayStruct []*MySubStruct `json:"field_array_struct"` | ||
} | ||
|
||
type MySubStruct struct { | ||
FieldIntPtr *int `json:"field_int_ptr"` | ||
FieldBool bool `json:"field_bool"` | ||
FieldString1 string `json:"field_string_sub1"` | ||
FieldString2 string `json:"field_string_sub2"` | ||
} | ||
|
||
func getPtr[T any](in T) *T { | ||
return &in | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package jsonpatch | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/ohler55/ojg/jp" | ||
) | ||
|
||
func (pr *PatchRequest[T]) remove(initialResource *T, emptyResource *T) (*T, error) { | ||
compiledPath, err := jp.ParseString(pr.Path) | ||
if err != nil { | ||
return nil, fmt.Errorf("fail to compile path %s %s", pr.Path, err.Error()) | ||
} | ||
|
||
b, err := json.Marshal(initialResource) | ||
if err != nil { | ||
return nil, fmt.Errorf("match fail to marshal input resource %s", err.Error()) | ||
} | ||
|
||
var resourceAsMap interface{} | ||
err = json.Unmarshal(b, &resourceAsMap) | ||
if err != nil { | ||
return nil, fmt.Errorf("match fail to unmarshal %s", err.Error()) | ||
} | ||
|
||
resourceAsMapModified, err := compiledPath.Remove(resourceAsMap) | ||
if err != nil { | ||
return nil, fmt.Errorf("error while deleting nodes %s", err.Error()) | ||
} | ||
|
||
return pr.remarshal(resourceAsMapModified, emptyResource) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package jsonpatch | ||
|
||
import ( | ||
"encoding/json" | ||
"reflect" | ||
"testing" | ||
) | ||
|
||
type applyRemoveTestCase[T any] struct { | ||
name string | ||
patches []*PatchRequest[MyStruct] | ||
input T | ||
newEmptyInputFunc func() T | ||
expectError bool | ||
expect T | ||
} | ||
|
||
func TestPatchRequest_applyRemove(t *testing.T) { | ||
testCases := []applyRemoveTestCase[*MyStruct]{ | ||
{ | ||
name: "remove_string", | ||
patches: []*PatchRequest[MyStruct]{ | ||
{ | ||
Operation: "remove", | ||
Path: "$.field_string", | ||
}, | ||
}, | ||
newEmptyInputFunc: func() *MyStruct { | ||
return &MyStruct{} | ||
}, | ||
input: &MyStruct{ | ||
FieldString: "foo", | ||
}, | ||
expectError: false, | ||
expect: &MyStruct{ | ||
FieldString: "", | ||
}, | ||
}, | ||
|
||
{ | ||
name: "remove_string_ptr", | ||
patches: []*PatchRequest[MyStruct]{ | ||
{ | ||
Operation: "remove", | ||
Path: "$.field_string_ptr", | ||
}, | ||
}, | ||
newEmptyInputFunc: func() *MyStruct { | ||
return &MyStruct{} | ||
}, | ||
input: &MyStruct{ | ||
FieldStringPtr: getPtr("foo"), | ||
}, | ||
expectError: false, | ||
expect: &MyStruct{ | ||
FieldStringPtr: nil, | ||
}, | ||
}, | ||
} | ||
for _, tt := range testCases { | ||
t.Run(tt.name, func(t *testing.T) { | ||
var err error | ||
var patched *MyStruct | ||
patched = tt.input | ||
for _, pr := range tt.patches { | ||
patched, err = pr.remove(patched, tt.newEmptyInputFunc()) | ||
} | ||
|
||
if (err != nil) != tt.expectError { | ||
t.Errorf("applyRemove() error = %v, expectError %v", err, tt.expectError) | ||
return | ||
} | ||
if !reflect.DeepEqual(patched, tt.expect) { | ||
bPatched, _ := json.Marshal(patched) | ||
bExpected, _ := json.Marshal(tt.expect) | ||
t.Errorf("applyRemove() got = %s", string(bPatched)) | ||
t.Errorf("applyRemove() expect = %s", string(bExpected)) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package jsonpatch | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/ohler55/ojg/jp" | ||
) | ||
|
||
func (pr *PatchRequest[T]) replace(resource *T, emptyResource *T) (*T, error) { | ||
compiledPath, err := jp.ParseString(pr.Path) | ||
if err != nil { | ||
return nil, fmt.Errorf("fail to compile path %s %s", pr.Path, err.Error()) | ||
} | ||
|
||
b, err := json.Marshal(resource) | ||
if err != nil { | ||
return nil, fmt.Errorf("match fail to marshal input resource %s", err.Error()) | ||
} | ||
|
||
var resourceAsMap interface{} | ||
err = json.Unmarshal(b, &resourceAsMap) | ||
if err != nil { | ||
return nil, fmt.Errorf("match fail to unmarshal %s", err.Error()) | ||
} | ||
|
||
err = compiledPath.Set(resourceAsMap, pr.Value) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return pr.remarshal(resourceAsMap, emptyResource) | ||
} |
Oops, something went wrong.