1
1
import abc
2
- from typing import Any
2
+ from typing import Any , Union
3
3
4
4
import jsonpatch
5
5
@@ -12,17 +12,32 @@ def __init__(self, path: list[str]) -> None:
12
12
self .path = path
13
13
14
14
@abc .abstractmethod
15
- def generate_patch (self , json_to_patch : dict | list ) -> jsonpatch .JsonPatch :
15
+ def generate_patch (self , contexts : list [ Union [ list , dict ]], prefix : list [ str ] = None ) -> jsonpatch .JsonPatch :
16
16
pass
17
17
18
+ def _format_path (self , path : list [str ], prefix : list [str ]) -> str :
19
+ """Converts the `path` to a string separated by "/" and starts also by "/"
20
+ If a prefix is defined and the path is not absolute, then the prefix is preprended.
21
+ An absolute path is one whose first element is "$"
22
+ """
23
+ if path [0 ] == "$" :
24
+ final_path = path [1 :]
25
+ elif prefix :
26
+ final_path = prefix + path
27
+ else :
28
+ final_path = path
29
+ final_path = [str (elem ) for elem in final_path ]
30
+ return "/" + "/" .join (final_path )
31
+
18
32
19
33
class JsonPatchAdd (JsonPatchOperator ):
20
34
def __init__ (self , path : list [str ], value : Any ) -> None :
21
35
super ().__init__ (path )
22
36
self .value = value
23
37
24
38
# Remember the op "add" is like an assignment
25
- def generate_patch (self , json_to_patch : dict | list ) -> jsonpatch .JsonPatch :
39
+ def generate_patch (self , contexts : list [Union [list , dict ]], prefix : list [str ] = None ) -> jsonpatch .JsonPatch :
40
+ json_to_patch = contexts [- 1 ]
26
41
# Check how many (nested) keys already exist
27
42
existing_path = []
28
43
first_non_existing_key = None
@@ -67,29 +82,25 @@ def generate_patch(self, json_to_patch: dict | list) -> jsonpatch.JsonPatch:
67
82
else :
68
83
new_value = {key : new_value }
69
84
70
- # Convert the list to a string separated by "/"
71
- formatted_path = "/" + "/" .join (new_path )
72
-
73
85
return jsonpatch .JsonPatch (
74
86
[
75
87
{
76
88
"op" : "add" ,
77
- "path" : formatted_path ,
89
+ "path" : self . _format_path ( new_path , prefix ) ,
78
90
"value" : new_value ,
79
91
}
80
92
]
81
93
)
82
94
83
95
84
96
class JsonPatchRemove (JsonPatchOperator ):
85
- def generate_patch (self , json_to_patch : dict | list ) -> jsonpatch .JsonPatch :
97
+ def generate_patch (self , contexts : list [ Union [ list , dict ]], prefix : list [ str ] = None ) -> jsonpatch .JsonPatch :
86
98
# TODO If the key to remove doesn't exist, this must become a no-op
87
- formatted_path = "/" + "/" .join (self .path )
88
99
return jsonpatch .JsonPatch (
89
100
[
90
101
{
91
102
"op" : "remove" ,
92
- "path" : formatted_path ,
103
+ "path" : self . _format_path ( self . path , prefix ) ,
93
104
}
94
105
]
95
106
)
@@ -100,41 +111,53 @@ def __init__(self, path: list[str], value: Any) -> None:
100
111
super ().__init__ (path )
101
112
self .value = value
102
113
103
- def generate_patch (self , json_to_patch : dict | list ) -> jsonpatch .JsonPatch :
104
- formatted_path = "/" + "/" .join (self .path )
105
- return jsonpatch .JsonPatch ([{"op" : "replace" , "path" : formatted_path , "value" : self .value }])
114
+ def generate_patch (self , contexts : list [Union [list , dict ]], prefix : list [str ] = None ) -> jsonpatch .JsonPatch :
115
+ return jsonpatch .JsonPatch (
116
+ [{"op" : "replace" , "path" : self ._format_path (self .path , prefix ), "value" : self .value }]
117
+ )
106
118
107
119
108
120
class JsonPatchCopy (JsonPatchOperator ):
109
121
def __init__ (self , path : list [str ], fromm : Any ) -> None :
110
122
super ().__init__ (path )
111
123
self .fromm = fromm
112
124
113
- def generate_patch (self , json_to_patch : dict | list ) -> jsonpatch .JsonPatch :
114
- formatted_path = "/" + "/" .join (self .path )
115
- formatted_from = "/" + "/" .join (self .fromm )
116
- return jsonpatch .JsonPatch ([{"op" : "copy" , "path" : formatted_path , "from" : formatted_from }])
125
+ def generate_patch (self , contexts : list [Union [list , dict ]], prefix : list [str ] = None ) -> jsonpatch .JsonPatch :
126
+ return jsonpatch .JsonPatch (
127
+ [
128
+ {
129
+ "op" : "copy" ,
130
+ "path" : self ._format_path (self .path , prefix ),
131
+ "from" : self ._format_path (self .fromm , prefix ),
132
+ }
133
+ ]
134
+ )
117
135
118
136
119
137
class JsonPatchMove (JsonPatchOperator ):
120
138
def __init__ (self , path : list [str ], fromm : Any ) -> None :
121
139
super ().__init__ (path )
122
140
self .fromm = fromm
123
141
124
- def generate_patch (self , json_to_patch : dict | list ) -> jsonpatch .JsonPatch :
125
- formatted_path = "/" + "/" .join (self .path )
126
- formatted_from = "/" + "/" .join (self .fromm )
127
- return jsonpatch .JsonPatch ([{"op" : "move" , "path" : formatted_path , "from" : formatted_from }])
142
+ def generate_patch (self , contexts : list [Union [list , dict ]], prefix : list [str ] = None ) -> jsonpatch .JsonPatch :
143
+ return jsonpatch .JsonPatch (
144
+ [
145
+ {
146
+ "op" : "move" ,
147
+ "path" : self ._format_path (self .path , prefix ),
148
+ "from" : self ._format_path (self .fromm , prefix ),
149
+ }
150
+ ]
151
+ )
128
152
129
153
130
154
class JsonPatchTest (JsonPatchOperator ):
131
155
def __init__ (self , path : list [str ], value : Any ) -> None :
132
156
super ().__init__ (path )
133
157
self .value = value
134
158
135
- def generate_patch (self , json_to_patch : dict | list ) -> jsonpatch .JsonPatch :
136
- formatted_path = "/" + "/" .join (self .path )
137
- return jsonpatch .JsonPatch ([{"op" : "test" , "path" : formatted_path , "value" : self .value }])
159
+ def generate_patch (self , contexts : list [Union [list , dict ]], prefix : list [str ] = None ) -> jsonpatch .JsonPatch :
160
+ return jsonpatch .JsonPatch ([{"op" : "test" , "path" : self ._format_path (self .path , prefix ), "value" : self .value }])
138
161
139
162
140
163
class JsonPatchExpr (JsonPatchOperator ):
@@ -147,7 +170,24 @@ def __init__(self, path: list[str], value: operators.Operator) -> None:
147
170
super ().__init__ (path )
148
171
self .value = value
149
172
150
- def generate_patch (self , json_to_patch : dict | list ) -> jsonpatch .JsonPatch :
151
- actual_value = self .value .get_value ([ json_to_patch ] )
173
+ def generate_patch (self , contexts : list [ Union [ list , dict ]], prefix : list [ str ] = None ) -> jsonpatch .JsonPatch :
174
+ actual_value = self .value .get_value (contexts )
152
175
json_patch_add = JsonPatchAdd (self .path , actual_value )
153
- return json_patch_add .generate_patch (json_to_patch )
176
+ return json_patch_add .generate_patch (contexts , prefix )
177
+
178
+
179
+ class JsonPatchForEach (JsonPatchOperator ):
180
+ """Generates a jsonpatch for each element from a list"""
181
+
182
+ def __init__ (self , op_with_ref : operators .OperatorWithRef , list_jsonpatch_op : list [JsonPatchOperator ]) -> None :
183
+ super ().__init__ ([])
184
+ self .op_with_ref = op_with_ref
185
+ self .list_jsonpatch_op = list_jsonpatch_op
186
+
187
+ def generate_patch (self , contexts : list [Union [list , dict ]], prefix : list [str ] = None ) -> jsonpatch .JsonPatch :
188
+ list_raw_patch = []
189
+ for payload , path in self .op_with_ref .get_value_with_ref (contexts ):
190
+ for jsonpatch_op in self .list_jsonpatch_op :
191
+ patch_obj = jsonpatch_op .generate_patch (contexts + [payload ], path )
192
+ list_raw_patch .extend (patch_obj .patch )
193
+ return jsonpatch .JsonPatch (list_raw_patch )
0 commit comments