blueprints: add `!If` tag (#4264)
* Added \!If tag * Fix typo * Removed trailing whitespace Signed-off-by: sdimovv <36302090+sdimovv@users.noreply.github.com> * format blueprint fixtures Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> Signed-off-by: sdimovv <36302090+sdimovv@users.noreply.github.com> Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org> Co-authored-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
3eecc76717
commit
8f3579ba45
|
@ -12,8 +12,13 @@ entries:
|
||||||
provider_type: github
|
provider_type: github
|
||||||
consumer_key: !Env foo
|
consumer_key: !Env foo
|
||||||
consumer_secret: !Env [bar, baz]
|
consumer_secret: !Env [bar, baz]
|
||||||
authentication_flow: !Find [authentik_flows.Flow, [slug, default-source-authentication]]
|
authentication_flow:
|
||||||
enrollment_flow: !Find [authentik_flows.Flow, [slug, default-source-enrollment]]
|
!Find [
|
||||||
|
authentik_flows.Flow,
|
||||||
|
[slug, default-source-authentication],
|
||||||
|
]
|
||||||
|
enrollment_flow:
|
||||||
|
!Find [authentik_flows.Flow, [slug, default-source-enrollment]]
|
||||||
- attrs:
|
- attrs:
|
||||||
expression: return True
|
expression: return True
|
||||||
identifiers:
|
identifiers:
|
||||||
|
@ -22,15 +27,70 @@ entries:
|
||||||
model: authentik_policies_expression.expressionpolicy
|
model: authentik_policies_expression.expressionpolicy
|
||||||
- attrs:
|
- attrs:
|
||||||
attributes:
|
attributes:
|
||||||
policy_pk1: !Format ["%s-%s", !Find [authentik_policies_expression.expressionpolicy, [!Context policy_property, !Context policy_property_value], [expression, return True]], suffix]
|
policy_pk1:
|
||||||
|
!Format [
|
||||||
|
"%s-%s",
|
||||||
|
!Find [
|
||||||
|
authentik_policies_expression.expressionpolicy,
|
||||||
|
[
|
||||||
|
!Context policy_property,
|
||||||
|
!Context policy_property_value,
|
||||||
|
],
|
||||||
|
[expression, return True],
|
||||||
|
],
|
||||||
|
suffix,
|
||||||
|
]
|
||||||
policy_pk2: !Format ["%s-%s", !KeyOf policy, suffix]
|
policy_pk2: !Format ["%s-%s", !KeyOf policy, suffix]
|
||||||
boolAnd: !Condition [AND, !Context foo, !Format ["%s", "a_string"], 1]
|
boolAnd:
|
||||||
boolNand: !Condition [NAND, !Context foo, !Format ["%s", "a_string"], 1]
|
!Condition [AND, !Context foo, !Format ["%s", "a_string"], 1]
|
||||||
boolOr: !Condition [OR, !Context foo, !Format ["%s", "a_string"], null]
|
boolNand:
|
||||||
boolNor: !Condition [NOR, !Context foo, !Format ["%s", "a_string"], null]
|
!Condition [NAND, !Context foo, !Format ["%s", "a_string"], 1]
|
||||||
boolXor: !Condition [XOR, !Context foo, !Format ["%s", "a_string"], 1]
|
boolOr:
|
||||||
boolXnor: !Condition [XNOR, !Context foo, !Format ["%s", "a_string"], 1]
|
!Condition [
|
||||||
boolComplex: !Condition [XNOR, !Condition [AND, !Context non_existing], !Condition [NOR, a string], !Condition [XOR, null]]
|
OR,
|
||||||
|
!Context foo,
|
||||||
|
!Format ["%s", "a_string"],
|
||||||
|
null,
|
||||||
|
]
|
||||||
|
boolNor:
|
||||||
|
!Condition [
|
||||||
|
NOR,
|
||||||
|
!Context foo,
|
||||||
|
!Format ["%s", "a_string"],
|
||||||
|
null,
|
||||||
|
]
|
||||||
|
boolXor:
|
||||||
|
!Condition [XOR, !Context foo, !Format ["%s", "a_string"], 1]
|
||||||
|
boolXnor:
|
||||||
|
!Condition [XNOR, !Context foo, !Format ["%s", "a_string"], 1]
|
||||||
|
boolComplex:
|
||||||
|
!Condition [
|
||||||
|
XNOR,
|
||||||
|
!Condition [AND, !Context non_existing],
|
||||||
|
!Condition [NOR, a string],
|
||||||
|
!Condition [XOR, null],
|
||||||
|
]
|
||||||
|
if_true_complex:
|
||||||
|
!If [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
dictionary:
|
||||||
|
{
|
||||||
|
with: { keys: "and_values" },
|
||||||
|
and_nested_custom_tags:
|
||||||
|
!Format ["foo-%s", !Context foo],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
]
|
||||||
|
if_false_complex:
|
||||||
|
!If [
|
||||||
|
!Condition [AND, false],
|
||||||
|
null,
|
||||||
|
[list, with, items, !Format ["foo-%s", !Context foo]],
|
||||||
|
]
|
||||||
|
if_true_simple: !If [!Context foo, true, text]
|
||||||
|
if_false_simple: !If [null, false, 2]
|
||||||
identifiers:
|
identifiers:
|
||||||
name: test
|
name: test
|
||||||
conditions:
|
conditions:
|
||||||
|
|
|
@ -153,6 +153,15 @@ class TestBlueprintsV1(TransactionTestCase):
|
||||||
"boolXor": True,
|
"boolXor": True,
|
||||||
"boolXnor": False,
|
"boolXnor": False,
|
||||||
"boolComplex": True,
|
"boolComplex": True,
|
||||||
|
"if_true_complex": {
|
||||||
|
"dictionary": {
|
||||||
|
"with": {"keys": "and_values"},
|
||||||
|
"and_nested_custom_tags": "foo-bar",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"if_false_complex": ["list", "with", "items", "foo-bar"],
|
||||||
|
"if_true_simple": True,
|
||||||
|
"if_false_simple": 2,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -312,6 +312,35 @@ class Condition(YAMLTag):
|
||||||
raise EntryInvalidError(exc)
|
raise EntryInvalidError(exc)
|
||||||
|
|
||||||
|
|
||||||
|
class If(YAMLTag):
|
||||||
|
"""Select YAML to use based on condition"""
|
||||||
|
|
||||||
|
condition: Any
|
||||||
|
when_true: Any
|
||||||
|
when_false: Any
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def __init__(self, loader: "BlueprintLoader", node: SequenceNode) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.condition = loader.construct_object(node.value[0])
|
||||||
|
self.when_true = loader.construct_object(node.value[1])
|
||||||
|
self.when_false = loader.construct_object(node.value[2])
|
||||||
|
|
||||||
|
def resolve(self, entry: BlueprintEntry, blueprint: Blueprint) -> Any:
|
||||||
|
if isinstance(self.condition, YAMLTag):
|
||||||
|
condition = self.condition.resolve(entry, blueprint)
|
||||||
|
else:
|
||||||
|
condition = self.condition
|
||||||
|
|
||||||
|
try:
|
||||||
|
return entry.tag_resolver(
|
||||||
|
self.when_true if condition else self.when_false,
|
||||||
|
blueprint,
|
||||||
|
)
|
||||||
|
except TypeError as exc:
|
||||||
|
raise EntryInvalidError(exc)
|
||||||
|
|
||||||
|
|
||||||
class BlueprintDumper(SafeDumper):
|
class BlueprintDumper(SafeDumper):
|
||||||
"""Dump dataclasses to yaml"""
|
"""Dump dataclasses to yaml"""
|
||||||
|
|
||||||
|
@ -353,6 +382,7 @@ class BlueprintLoader(SafeLoader):
|
||||||
self.add_constructor("!Context", Context)
|
self.add_constructor("!Context", Context)
|
||||||
self.add_constructor("!Format", Format)
|
self.add_constructor("!Format", Format)
|
||||||
self.add_constructor("!Condition", Condition)
|
self.add_constructor("!Condition", Condition)
|
||||||
|
self.add_constructor("!If", If)
|
||||||
self.add_constructor("!Env", Env)
|
self.add_constructor("!Env", Env)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,42 @@ Example: `name: !Format [my-policy-%s, !Context instance_name]`
|
||||||
|
|
||||||
Format a string using python's % formatting. First argument is the format string, any remaining arguments are used for formatting.
|
Format a string using python's % formatting. First argument is the format string, any remaining arguments are used for formatting.
|
||||||
|
|
||||||
|
#### `!If`
|
||||||
|
|
||||||
|
Minimal example:
|
||||||
|
|
||||||
|
`required: !If [true, true, false] # !If [<condition>, <when true>, <when false>`
|
||||||
|
|
||||||
|
Full example:
|
||||||
|
|
||||||
|
```
|
||||||
|
attributes: !If [
|
||||||
|
!Condition [...], # Or any valid YAML or custom tag. Evaluated as boolean in Python
|
||||||
|
{ # When condition evaluates to true
|
||||||
|
dictionary:
|
||||||
|
{
|
||||||
|
with:
|
||||||
|
{
|
||||||
|
keys: "and_values"
|
||||||
|
},
|
||||||
|
and_nested_custom_tags: !Format ["foo-%s", !Context foo]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[ # When condition evaluates to false
|
||||||
|
list,
|
||||||
|
with,
|
||||||
|
items,
|
||||||
|
!Format ["foo-%s", !Context foo]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Conditionally add YAML to a blueprint.
|
||||||
|
|
||||||
|
Similar to a one-line if, the first argument is the condition, which can be any valid yaml or custom tag. It will be evaluted as boolean in python. However, keep in mind that dictionaries and lists will always evaluate to `true`, unless they are empty.
|
||||||
|
|
||||||
|
The second argument is used when the condition is `true`, and the third - when `false`. The YAML inside both arguments will be fully resolved, thus it is possible to use custom YAML tags and even nest them inside dictionaries and lists.
|
||||||
|
|
||||||
#### `!Condition`
|
#### `!Condition`
|
||||||
|
|
||||||
Minimal example:
|
Minimal example:
|
||||||
|
@ -68,4 +104,4 @@ Requires at least one argument after the mode selection.
|
||||||
|
|
||||||
If only a single argument is provided, its boolean representation will be returned for all normal modes and its negated boolean representation will be returned for all negated modes.
|
If only a single argument is provided, its boolean representation will be returned for all normal modes and its negated boolean representation will be returned for all negated modes.
|
||||||
|
|
||||||
Normally, it should be used to define complex conditions for the `conditions` attribute of a blueprint entry (see [the blueprint file structure](./structure.md)). However, this is essentially just a boolean evaluator so it can be used everywhere a boolean representation is required.
|
Normally, it should be used to define complex conditions for use with an `!If` tag or for the `conditions` attribute of a blueprint entry (see [the blueprint file structure](./structure.md)). However, this is essentially just a boolean evaluator so it can be used everywhere a boolean representation is required.
|
||||||
|
|
Reference in New Issue