Skip to content

Commit

Permalink
Merge pull request #90 from Peefy/add-more-patch-and-loop-modules
Browse files Browse the repository at this point in the history
feat: add more modules about merge and loop
  • Loading branch information
Peefy authored Dec 4, 2023
2 parents 782e35e + 484062a commit 7bb7cda
Show file tree
Hide file tree
Showing 15 changed files with 334 additions and 0 deletions.
61 changes: 61 additions & 0 deletions json_merge_patch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
## Introduction

`json_merge_patch` is a module for applying JSON merge patches (RFC 7368) for KCL values.

## How to Use

+ Add the dependency

```shell
kcl mod add json_merge_patch
```

+ Write the code

```python
import json_merge_patch as p

data1 = {
"firstName": "John",
"lastName": "Doe",
"age": 30,
"address": {
"streetAddress": "1234 Main St",
"city": "New York",
"state": "NY",
"postalCode": "10001"
},
"phoneNumbers": [
{
"type": "home",
"number": "212-555-1234"
},
{
"type": "work",
"number": "646-555-5678"
}
]
}
data2 = {
"firstName": "John",
"lastName": "Doe",
"age": 30,
"address": {
"streetAddress": "1234 Main St",
"city": "New York",
"state": "NY",
"postalCode": None
},
"phoneNumbers": [
{
"type": "work",
"number": "646-555-5678"
}
]
}
data_merge = p.merge(data1, data2)
```

## Resource

The code source and documents are [here](https://github.com/kcl-lang/artifacthub/tree/main/json_merge_patch)
5 changes: 5 additions & 0 deletions json_merge_patch/kcl.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "json_merge_patch"
version = "0.1.0"
description = "`json_merge_patch` is a module for applying JSON merge patches (RFC 7368) for KCL values."

Empty file added json_merge_patch/kcl.mod.lock
Empty file.
58 changes: 58 additions & 0 deletions json_merge_patch/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
KCL_BUILTIN_TYPES = ["int", "str", "bool", "float", "None", "UndefinedType", "any", "list", "dict", "function", "number_multiplier"]
NULL_CONSTANTS = [Undefined, None]

is_schema = lambda obj: any -> bool {
typeof(obj) not in KCL_BUILTIN_TYPES

}
is_config = lambda obj: any -> bool {
typeof(obj) == "dict" or is_schema(obj)
}

_looper_n = lambda elements: [any], n: int, func: (any, any) -> any, initial: any-> any {
assert n >= 0
result = initial
if n < len(elements):
result = _looper_n(elements, n + 1, func, func(result, elements[n]))
result
}

looper = lambda initial: any, elements: [any], func: (any, any) -> any -> any {
_looper_n(elements, 0, func, initial)
}

for_each = lambda elements: [any], func: (any) -> any {
_looper_n(elements, 0, lambda v, e {
func(e)
}, Undefined)
}

looper_enumerate = lambda initial: any, elements: [any] | {str:}, func: (any, str | int, any) -> any -> any {
looper(initial, [{"k" = k, "v" = v} for k, v in elements], lambda initial, value {
func(initial, value.k, value.v)
})
}

merge = lambda src: any, obj: any -> any {
result = src
if not is_config(src):
result = {}
if not is_config(obj):
result = obj
else:
result = looper_enumerate(result, obj, lambda result, key, value {
target = result[key]
if is_config(value):
if is_config(target):
result |= {"{}".format(key) = merge(target, value)}
else:
result |= {"{}".format(key) = merge({}, value)}
elif value in NULL_CONSTANTS:
result |= {"{}".format(key) = Undefined}
result = {k: v for k, v in result if k != key}
else:
result |= {"{}".format(key) = value}
result
})
result
}
71 changes: 71 additions & 0 deletions json_merge_patch/main_test.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
test_merge = lambda {
cases: [[]] = [
[
{
"title": "Goodbye!"
"author": {
"givenName": "John"
"familyName": "Doe"
}
"tags": ["example", "sample"]
"content": "This will be unchanged"
}
{
"title": "Hello!"
"phoneNumber": "+01-123-456-7890"
"author": {"familyName": None}
"tags": ["example"]
}
{
"title": "Hello!"
"author": {"givenName": "John"}
"tags": ["example"]
"content": "This will be unchanged"
"phoneNumber": "+01-123-456-7890"
}
]
[{"a": "b"}, {"a": "c"}, {"a": "c"}]
[{"a": "b"}, {"b": "c"}, {"a": "b", "b": "c"}]
[{"a": "b"}, {"a": None}, {}]
[{"a": "b", "b": "c"}, {"a": None}, {"b": "c"}]
[{"a": ["b"]}, {"a": "c"}, {"a": "c"}]
[{"a": "c"}, {"a": ["b"]}, {"a": ["b"]}]
[
{
"a": {"b": "c"}
}
{
"a": {"b": "d", "c": None}
}
{
"a": {"b": "d"}
}
]
[{"a": [{"b": "c"}]}, {"a": [1]}, {"a": [1]}]
[["a", "b"], ["c", "d"], ["c", "d"]]
[{"a": "foo"}, None, None]
[{"a": "foo"}, "bar", "bar"]
[{"e": None}, {"a": 1}, {"e": None, "a": 1}]
[[1, 2], {"a": "b", "c": None}, {"a": "b"}]
[
{}
{
"a": {
"bb": {"ccc": None}
}
}
{
"a": {
"bb": {}
}
}
]
]
for_each(cases, lambda case {
data1 = case[0]
data2 = case[1]
expected = case[2]
got = merge(data1, data2)
assert str(got) == str(expected), "expected ${expected}, got ${got}"
})
}
7 changes: 7 additions & 0 deletions looper/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Introduction

`looper` is a KCL loop library

## Resource

The Code source and documents are [here](https://github.com/kcl-lang/modules/tree/main/looper)
5 changes: 5 additions & 0 deletions looper/kcl.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "looper"
version = "0.0.1"
description = "`looper` is a KCL loop library"

Empty file added looper/kcl.mod.lock
Empty file.
27 changes: 27 additions & 0 deletions looper/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
_looper_n = lambda elements: [any], n: int, func: (any, any) -> any, initial: any-> any {
assert n >= 0
result = initial
if n < len(elements):
result = _looper_n(elements, n + 1, func, func(result, elements[n]))
result
}

looper = lambda initial: any, elements: [any], func: (any, any) -> any -> any {
_looper_n(elements, 0, func, initial)
}

for_each = lambda elements: [any], func: (any) -> any {
[func(i) for i in elements]
Undefined
}

looper_enumerate = lambda initial: any, elements: [any] | {str:}, func: (any, str | int, any) -> any -> any {
looper(initial, [{"k" = k, "v" = v} for k, v in elements], lambda initial, value {
func(initial, value.k, value.v)
})
}

for_each_looper_enumerate = lambda elements: [any] | {str:}, func: (str | int, any) -> any -> any {
[func(k, v) for k, v in elements]
Undefined
}
28 changes: 28 additions & 0 deletions looper/main_test.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
test_merge = lambda {
cases: [[]] = [
[
0
[1, 2, 3]
lambda v, i {
v + i
}
6
]
[
1
[1, 2, 3]
lambda v, i {
v * i
}
6
]
]
for_each(cases, lambda case {
initial = case[0]
values = case[1]
func = case[2]
expected = case[3]
got = looper(initial, values, func)
assert got == expected, "expected ${expected}, got ${got}"
})
}
7 changes: 7 additions & 0 deletions update-image-tag/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Introduction

`update-image-tag` is a KCL mutation module to add istio sidecar inject labels for `Namespace` resources.

## Resource

The Code sources and documents are [here](https://github.com/kcl-lang/modules/tree/main/update-image-tag)
6 changes: 6 additions & 0 deletions update-image-tag/kcl.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "update-image-tag"
edition = "*"
version = "0.1.0"
description = "`update-image-tag` is a KCL mutation module"

Empty file added update-image-tag/kcl.mod.lock
Empty file.
39 changes: 39 additions & 0 deletions update-image-tag/main.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
schema Params:
deployName: str
containerName: str
image: str

params: Params = option("params")

_looper_n = lambda elements: [any], n: int, func: (any, any) -> any, initial: any-> any {
assert n >= 0
result = initial
if n < len(elements):
result = _looper_n(elements, n + 1, func, func(result, elements[n]))
result
}

looper = lambda initial: any, elements: [any], func: (any, any) -> any -> any {
_looper_n(elements, 0, func, initial)
}

strategy_merge_patches_with_name = lambda containers: [{str:}], patches: [{str:}] -> [{str:}] {
looper(containers, patches, lambda containers: [{str:}], patch: {str:} {
patch_index_list = [i for i, c in containers if patch.name and c.name == patch.name]
if patch_index_list:
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]
else:
patched_containers = containers + [patch]
patched_containers

})

}
items = [item | {
if item.kind == "Deployment" and params?.deployName and params?.containerName and item.metadata.name == params?.deployName:
spec.template.spec.containers = strategy_merge_patches_with_name(item.spec.template.spec.containers, [{
name = params?.containerName
image = params?.image
}])

} for item in option("items") or []]
20 changes: 20 additions & 0 deletions update-image-tag/main_test.k
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
test_strategy_merge_patches_with_name = lambda {
containers = [
{
name = "foo"
image = "foo:1.10"
}
{
name = "bar"
image = "bar:1.20"
}
]
assert strategy_merge_patches_with_name(containers, [{
name = "foo"
image = "foofoo"
}])[0].image == "foofoo"
assert strategy_merge_patches_with_name(containers, [{
name = "foobar"
image = "foofoo"
}])[2].image == "foofoo"
}

0 comments on commit 7bb7cda

Please sign in to comment.