Skip to content

Commit 7bb7cda

Browse files
authoredDec 4, 2023
Merge pull request #90 from Peefy/add-more-patch-and-loop-modules
feat: add more modules about merge and loop
2 parents 782e35e + 484062a commit 7bb7cda

15 files changed

+334
-0
lines changed
 

‎json_merge_patch/README.md

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
## Introduction
2+
3+
`json_merge_patch` is a module for applying JSON merge patches (RFC 7368) for KCL values.
4+
5+
## How to Use
6+
7+
+ Add the dependency
8+
9+
```shell
10+
kcl mod add json_merge_patch
11+
```
12+
13+
+ Write the code
14+
15+
```python
16+
import json_merge_patch as p
17+
18+
data1 = {
19+
"firstName": "John",
20+
"lastName": "Doe",
21+
"age": 30,
22+
"address": {
23+
"streetAddress": "1234 Main St",
24+
"city": "New York",
25+
"state": "NY",
26+
"postalCode": "10001"
27+
},
28+
"phoneNumbers": [
29+
{
30+
"type": "home",
31+
"number": "212-555-1234"
32+
},
33+
{
34+
"type": "work",
35+
"number": "646-555-5678"
36+
}
37+
]
38+
}
39+
data2 = {
40+
"firstName": "John",
41+
"lastName": "Doe",
42+
"age": 30,
43+
"address": {
44+
"streetAddress": "1234 Main St",
45+
"city": "New York",
46+
"state": "NY",
47+
"postalCode": None
48+
},
49+
"phoneNumbers": [
50+
{
51+
"type": "work",
52+
"number": "646-555-5678"
53+
}
54+
]
55+
}
56+
data_merge = p.merge(data1, data2)
57+
```
58+
59+
## Resource
60+
61+
The code source and documents are [here](https://github.com/kcl-lang/artifacthub/tree/main/json_merge_patch)

‎json_merge_patch/kcl.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
name = "json_merge_patch"
3+
version = "0.1.0"
4+
description = "`json_merge_patch` is a module for applying JSON merge patches (RFC 7368) for KCL values."
5+

‎json_merge_patch/kcl.mod.lock

Whitespace-only changes.

‎json_merge_patch/main.k

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
KCL_BUILTIN_TYPES = ["int", "str", "bool", "float", "None", "UndefinedType", "any", "list", "dict", "function", "number_multiplier"]
2+
NULL_CONSTANTS = [Undefined, None]
3+
4+
is_schema = lambda obj: any -> bool {
5+
typeof(obj) not in KCL_BUILTIN_TYPES
6+
7+
}
8+
is_config = lambda obj: any -> bool {
9+
typeof(obj) == "dict" or is_schema(obj)
10+
}
11+
12+
_looper_n = lambda elements: [any], n: int, func: (any, any) -> any, initial: any-> any {
13+
assert n >= 0
14+
result = initial
15+
if n < len(elements):
16+
result = _looper_n(elements, n + 1, func, func(result, elements[n]))
17+
result
18+
}
19+
20+
looper = lambda initial: any, elements: [any], func: (any, any) -> any -> any {
21+
_looper_n(elements, 0, func, initial)
22+
}
23+
24+
for_each = lambda elements: [any], func: (any) -> any {
25+
_looper_n(elements, 0, lambda v, e {
26+
func(e)
27+
}, Undefined)
28+
}
29+
30+
looper_enumerate = lambda initial: any, elements: [any] | {str:}, func: (any, str | int, any) -> any -> any {
31+
looper(initial, [{"k" = k, "v" = v} for k, v in elements], lambda initial, value {
32+
func(initial, value.k, value.v)
33+
})
34+
}
35+
36+
merge = lambda src: any, obj: any -> any {
37+
result = src
38+
if not is_config(src):
39+
result = {}
40+
if not is_config(obj):
41+
result = obj
42+
else:
43+
result = looper_enumerate(result, obj, lambda result, key, value {
44+
target = result[key]
45+
if is_config(value):
46+
if is_config(target):
47+
result |= {"{}".format(key) = merge(target, value)}
48+
else:
49+
result |= {"{}".format(key) = merge({}, value)}
50+
elif value in NULL_CONSTANTS:
51+
result |= {"{}".format(key) = Undefined}
52+
result = {k: v for k, v in result if k != key}
53+
else:
54+
result |= {"{}".format(key) = value}
55+
result
56+
})
57+
result
58+
}

‎json_merge_patch/main_test.k

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
test_merge = lambda {
2+
cases: [[]] = [
3+
[
4+
{
5+
"title": "Goodbye!"
6+
"author": {
7+
"givenName": "John"
8+
"familyName": "Doe"
9+
}
10+
"tags": ["example", "sample"]
11+
"content": "This will be unchanged"
12+
}
13+
{
14+
"title": "Hello!"
15+
"phoneNumber": "+01-123-456-7890"
16+
"author": {"familyName": None}
17+
"tags": ["example"]
18+
}
19+
{
20+
"title": "Hello!"
21+
"author": {"givenName": "John"}
22+
"tags": ["example"]
23+
"content": "This will be unchanged"
24+
"phoneNumber": "+01-123-456-7890"
25+
}
26+
]
27+
[{"a": "b"}, {"a": "c"}, {"a": "c"}]
28+
[{"a": "b"}, {"b": "c"}, {"a": "b", "b": "c"}]
29+
[{"a": "b"}, {"a": None}, {}]
30+
[{"a": "b", "b": "c"}, {"a": None}, {"b": "c"}]
31+
[{"a": ["b"]}, {"a": "c"}, {"a": "c"}]
32+
[{"a": "c"}, {"a": ["b"]}, {"a": ["b"]}]
33+
[
34+
{
35+
"a": {"b": "c"}
36+
}
37+
{
38+
"a": {"b": "d", "c": None}
39+
}
40+
{
41+
"a": {"b": "d"}
42+
}
43+
]
44+
[{"a": [{"b": "c"}]}, {"a": [1]}, {"a": [1]}]
45+
[["a", "b"], ["c", "d"], ["c", "d"]]
46+
[{"a": "foo"}, None, None]
47+
[{"a": "foo"}, "bar", "bar"]
48+
[{"e": None}, {"a": 1}, {"e": None, "a": 1}]
49+
[[1, 2], {"a": "b", "c": None}, {"a": "b"}]
50+
[
51+
{}
52+
{
53+
"a": {
54+
"bb": {"ccc": None}
55+
}
56+
}
57+
{
58+
"a": {
59+
"bb": {}
60+
}
61+
}
62+
]
63+
]
64+
for_each(cases, lambda case {
65+
data1 = case[0]
66+
data2 = case[1]
67+
expected = case[2]
68+
got = merge(data1, data2)
69+
assert str(got) == str(expected), "expected ${expected}, got ${got}"
70+
})
71+
}

‎looper/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Introduction
2+
3+
`looper` is a KCL loop library
4+
5+
## Resource
6+
7+
The Code source and documents are [here](https://github.com/kcl-lang/modules/tree/main/looper)

‎looper/kcl.mod

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[package]
2+
name = "looper"
3+
version = "0.0.1"
4+
description = "`looper` is a KCL loop library"
5+

‎looper/kcl.mod.lock

Whitespace-only changes.

‎looper/main.k

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
_looper_n = lambda elements: [any], n: int, func: (any, any) -> any, initial: any-> any {
2+
assert n >= 0
3+
result = initial
4+
if n < len(elements):
5+
result = _looper_n(elements, n + 1, func, func(result, elements[n]))
6+
result
7+
}
8+
9+
looper = lambda initial: any, elements: [any], func: (any, any) -> any -> any {
10+
_looper_n(elements, 0, func, initial)
11+
}
12+
13+
for_each = lambda elements: [any], func: (any) -> any {
14+
[func(i) for i in elements]
15+
Undefined
16+
}
17+
18+
looper_enumerate = lambda initial: any, elements: [any] | {str:}, func: (any, str | int, any) -> any -> any {
19+
looper(initial, [{"k" = k, "v" = v} for k, v in elements], lambda initial, value {
20+
func(initial, value.k, value.v)
21+
})
22+
}
23+
24+
for_each_looper_enumerate = lambda elements: [any] | {str:}, func: (str | int, any) -> any -> any {
25+
[func(k, v) for k, v in elements]
26+
Undefined
27+
}

‎looper/main_test.k

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
test_merge = lambda {
2+
cases: [[]] = [
3+
[
4+
0
5+
[1, 2, 3]
6+
lambda v, i {
7+
v + i
8+
}
9+
6
10+
]
11+
[
12+
1
13+
[1, 2, 3]
14+
lambda v, i {
15+
v * i
16+
}
17+
6
18+
]
19+
]
20+
for_each(cases, lambda case {
21+
initial = case[0]
22+
values = case[1]
23+
func = case[2]
24+
expected = case[3]
25+
got = looper(initial, values, func)
26+
assert got == expected, "expected ${expected}, got ${got}"
27+
})
28+
}

‎update-image-tag/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Introduction
2+
3+
`update-image-tag` is a KCL mutation module to add istio sidecar inject labels for `Namespace` resources.
4+
5+
## Resource
6+
7+
The Code sources and documents are [here](https://github.com/kcl-lang/modules/tree/main/update-image-tag)

‎update-image-tag/kcl.mod

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "update-image-tag"
3+
edition = "*"
4+
version = "0.1.0"
5+
description = "`update-image-tag` is a KCL mutation module"
6+

‎update-image-tag/kcl.mod.lock

Whitespace-only changes.

‎update-image-tag/main.k

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
schema Params:
2+
deployName: str
3+
containerName: str
4+
image: str
5+
6+
params: Params = option("params")
7+
8+
_looper_n = lambda elements: [any], n: int, func: (any, any) -> any, initial: any-> any {
9+
assert n >= 0
10+
result = initial
11+
if n < len(elements):
12+
result = _looper_n(elements, n + 1, func, func(result, elements[n]))
13+
result
14+
}
15+
16+
looper = lambda initial: any, elements: [any], func: (any, any) -> any -> any {
17+
_looper_n(elements, 0, func, initial)
18+
}
19+
20+
strategy_merge_patches_with_name = lambda containers: [{str:}], patches: [{str:}] -> [{str:}] {
21+
looper(containers, patches, lambda containers: [{str:}], patch: {str:} {
22+
patch_index_list = [i for i, c in containers if patch.name and c.name == patch.name]
23+
if patch_index_list:
24+
patched_containers = containers | [patch if patch.name and c.name == patch.name else {} for i, c in containers if patch.name and c.name == patch.name]
25+
else:
26+
patched_containers = containers + [patch]
27+
patched_containers
28+
29+
})
30+
31+
}
32+
items = [item | {
33+
if item.kind == "Deployment" and params?.deployName and params?.containerName and item.metadata.name == params?.deployName:
34+
spec.template.spec.containers = strategy_merge_patches_with_name(item.spec.template.spec.containers, [{
35+
name = params?.containerName
36+
image = params?.image
37+
}])
38+
39+
} for item in option("items") or []]

‎update-image-tag/main_test.k

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
test_strategy_merge_patches_with_name = lambda {
2+
containers = [
3+
{
4+
name = "foo"
5+
image = "foo:1.10"
6+
}
7+
{
8+
name = "bar"
9+
image = "bar:1.20"
10+
}
11+
]
12+
assert strategy_merge_patches_with_name(containers, [{
13+
name = "foo"
14+
image = "foofoo"
15+
}])[0].image == "foofoo"
16+
assert strategy_merge_patches_with_name(containers, [{
17+
name = "foobar"
18+
image = "foofoo"
19+
}])[2].image == "foofoo"
20+
}

0 commit comments

Comments
 (0)
Please sign in to comment.