Skip to content

Instantly share code, notes, and snippets.

@pivotaljohn
Last active April 4, 2021 17:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pivotaljohn/15b584fed986b9cc189b203adbb39af9 to your computer and use it in GitHub Desktop.
Save pivotaljohn/15b584fed986b9cc189b203adbb39af9 to your computer and use it in GitHub Desktop.
RFC 6902 "JSON Patch" implemented in `ytt`
---
kind: Package
spec:
template:
spec:
fetch:
- imgpkgBundle:
image: base-image
load("@ytt:struct", "struct")
load("@ytt:yaml", "yaml")
# `raw_yaml()` makes an editable copy of `node`
# warning: all nodes are scrubbed of attached annotations and code.
def raw_yaml(node):
return yaml.decode(yaml.encode(node))
end
def key_or_index(target, part):
if type(target) == type([]):
part = len(target) if part == "-" else int(part)
end
return part
end
# `at()` returns:
# 1. the penultimate descendant located along `path` from `node`
# 2. the key/index to the ultimate descenant
def at(node, path):
parts = path.split("/")[1:]
target = node
for part in parts[:-1]:
target = target[key_or_index(target, part)]
end
return (target, key_or_index(target, parts[-1]))
end
def edit(left, path, right, op):
copy = raw_yaml(left)
(target, key) = at(copy, path)
op(target, key, right)
return copy
end
def add(target, key, right):
if type(target) == type([]):
target.insert(key, right)
end
target[key] = right
end
def remove(target, key, _):
target.pop(key)
end
def replace(target, key, right):
target[key] = right
end
def move(left, src, dest):
(target, key) = at(raw_yaml(left), src)
copy = edit(left, src, None, remove)
copy = edit(copy, dest, target[key], add)
return copy
end
def jsonpath_add(path):
return lambda left, right: edit(left, path, right, add)
end
def jsonpath_remove(path):
return lambda left, right: edit(left, path, right, remove)
end
def jsonpath_replace(path):
return lambda left, right: edit(left, path, right, replace)
end
def jsonpath_move(src, dest):
return lambda left, right: move(left, src, dest)
end
jsonpatch = struct.make(add=jsonpath_add, move=jsonpath_move, remove=jsonpath_remove, replace=jsonpath_replace)
#@ load("@ytt:overlay", "overlay")
#@ load("jsonpatch.star", "jsonpatch")
#@overlay/match by=overlay.subset({"kind": "Package"})
#@overlay/replace via=jsonpatch.replace("/spec/template/spec/fetch/0/imgpkgBundle/image")
---
dev_image
#@ load("@ytt:overlay", "overlay")
#@ load("jsonpatch.star", "jsonpatch")
---
id: A.1
name: Adding an Object Member
foo: bar
#@overlay/match by=overlay.subset({"id": "A.1"})
#@overlay/replace via=jsonpatch.add("/baz")
---
qux
#@overlay/match by=overlay.subset({"id": "A.1"})
#@overlay/assert
---
baz: qux
foo: bar
...
---
id: A.2
name: Adding an Array Element
foo:
- bar
- baz
#@overlay/match by=overlay.subset({"id": "A.2"})
#@overlay/replace via=jsonpatch.add("/foo/1")
---
qux
#@overlay/match by=overlay.subset({"id": "A.2"})
#@overlay/assert
---
foo:
#@overlay/assert
- bar
#@overlay/assert
- qux
#@overlay/assert
- baz
...
---
id: A.3
name: Removing an Object Member
baz: qux
foo: bar
#@overlay/match by=overlay.subset({"id": "A.3"})
#@overlay/replace via=jsonpatch.remove("/baz")
---
#@overlay/match by=overlay.subset({"id": "A.3"})
#@overlay/assert
---
foo: bar
...
---
id: A.4
name: Removing an Array Element
foo: ["bar", "quz", "baz"]
#@overlay/match by=overlay.subset({"id": "A.4"})
#@overlay/replace via=jsonpatch.remove("/foo/1")
---
#@overlay/match by=overlay.subset({"id": "A.4"})
#@overlay/assert
---
foo:
#@overlay/assert
- bar
#@overlay/assert
- baz
...
---
id: A.5
name: Replacing a Value
baz: qux
foo: bar
#@overlay/match by=overlay.subset({"id": "A.5"})
#@overlay/replace via=jsonpatch.replace("/baz")
---
boo
#@overlay/match by=overlay.subset({"id": "A.5"})
#@overlay/assert
---
baz: boo
foo: bar
...
---
id: A.6
name: Moving a Value
foo:
bar: baz
waldo: fred
qux:
corge: grault
#@overlay/match by=overlay.subset({"id": "A.6"})
#@overlay/replace via=jsonpatch.move("/foo/waldo","/qux/thud")
---
#@overlay/match by=overlay.subset({"id": "A.6"})
---
foo:
#@overlay/assert
bar: baz
qux:
#@overlay/assert
corge: grault
#@overlay/assert
thud: fred
...
---
id: A.7
name: Moving an Array Element
foo:
- all
- grass
- cows
- eat
#@overlay/match by=overlay.subset({"id": "A.7"})
#@overlay/replace via=jsonpatch.move("/foo/1", "/foo/3")
---
#@overlay/match by=overlay.subset({"id": "A.7"})
---
#@overlay/assert
foo:
#@overlay/assert
- all
#@overlay/assert
- cows
#@overlay/assert
- eat
#@overlay/assert
- grass
...
---
id: A.10
name: Adding a Nested Member Object
foo: bar
#@overlay/match by=overlay.subset({"id": "A.10"})
#@overlay/replace via=jsonpatch.add("/child")
---
grandchild: {}
#@overlay/match by=overlay.subset({"id": "A.10"})
---
#@overlay/assert
foo: bar
#@overlay/assert
child:
#@overlay/assert
grandchild: {}
...
---
id: A.16
name: Adding an Array Value
foo:
- bar
#@overlay/match by=overlay.subset({"id": "A.16"})
#@overlay/replace via=jsonpatch.add("/foo/-")
---
- abc
- def
#@overlay/match by=overlay.subset({"id": "A.16"})
---
foo:
#@overlay/assert
- bar
#@overlay/assert
-
#@overlay/assert
- abc
#@overlay/assert
- def
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment