Skip to content

Commit

Permalink
Merge pull request #919 from samj1912/fix-json-patch
Browse files Browse the repository at this point in the history
Fix incorrect JSONPatch paths when special characters are used
  • Loading branch information
nolar authored Nov 1, 2022
2 parents 5c6f68c + 85b132d commit bab2d4c
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 3 deletions.
14 changes: 11 additions & 3 deletions kopf/_cogs/structs/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
JSONPatchOp = Literal["add", "replace", "remove"]


def _escaped_path(keys: List[str]) -> str:
"""Provides an appropriately escaped path for JSON Patches.
See https://datatracker.ietf.org/doc/html/rfc6901#section-3 for more details.
"""
return '/'.join(map(lambda key: key.replace('~', '~0').replace('/', '~1'), keys))


class JSONPatchItem(TypedDict, total=False):
op: JSONPatchOp
path: str
Expand Down Expand Up @@ -91,14 +99,14 @@ def as_json_patch(self) -> JSONPatch:
def _as_json_patch(self, value: object, keys: List[str]) -> JSONPatch:
result: JSONPatch = []
if value is None:
result.append(JSONPatchItem(op='remove', path='/'.join(keys)))
result.append(JSONPatchItem(op='remove', path=_escaped_path(keys)))
elif len(keys) > 1 and self._original and not self._is_in_original_path(keys):
result.append(JSONPatchItem(op='add', path='/'.join(keys), value=value))
result.append(JSONPatchItem(op='add', path=_escaped_path(keys), value=value))
elif isinstance(value, collections.abc.Mapping) and value:
for key, val in value.items():
result.extend(self._as_json_patch(val, keys + [key]))
else:
result.append(JSONPatchItem(op='replace', path='/'.join(keys), value=value))
result.append(JSONPatchItem(op='replace', path=_escaped_path(keys), value=value))
return result

def _is_in_original_path(self, keys: List[str]) -> bool:
Expand Down
18 changes: 18 additions & 0 deletions tests/admission/test_jsonpatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,21 @@ def test_removal_of_the_subkey():
assert jsonpatch == [
{'op': 'remove', 'path': '/xyz/abc'},
]


def test_escaping_of_key():
patch = Patch()
patch['~xyz/test'] = {'abc': None}
jsonpatch = patch.as_json_patch()
assert jsonpatch == [
{'op': 'remove', 'path': '/~0xyz~1test/abc'}
]


def test_recursive_escape_of_key():
patch = Patch()
patch['x/y/~z'] = {'a/b/~0c': None}
jsonpatch = patch.as_json_patch()
assert jsonpatch == [
{'op': 'remove', 'path': '/x~1y~1~0z/a~1b~1~00c'},
]

0 comments on commit bab2d4c

Please sign in to comment.