From 569c6d626dd4cd40ddb2cffc2ff4f411d3a05638 Mon Sep 17 00:00:00 2001 From: Johan Fylling Date: Fri, 22 Sep 2023 14:31:23 +0200 Subject: [PATCH] docs: Documenting general refs in rule heads Fixes: #5996 Signed-off-by: Johan Fylling --- docs/content/policy-language.md | 83 +++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/docs/content/policy-language.md b/docs/content/policy-language.md index 043115b110..072fd3c27e 100644 --- a/docs/content/policy-language.md +++ b/docs/content/policy-language.md @@ -992,15 +992,11 @@ data.example ```live:eg/ref_heads:output ``` -#### General References +#### Variables in Rule Head References -Any term, except the very first, in a rule head's reference can be a variable. These variables can be assigned within the rule, just as for any other partial rule, to dynamically construct a nested collection of objects. +Any term, except the very first, in a rule head's reference can be a variable. These variables can be assigned within the rule, just as for any other partial rule, to dynamically construct a nested collection of objects. -{{< danger >}} -General refs in rule heads is an experimental feature, and can be enabled by setting the `EXPERIMENTAL_GENERAL_RULE_REFS` environment variable. - -This feature is currently not supported for Wasm and IR. -{{< /danger >}} +##### Example Data: @@ -1031,7 +1027,7 @@ Data: } ``` -Module: +Module: ```rego package example @@ -1064,7 +1060,7 @@ Query: data.example ``` -Output: +Output: ```json { @@ -1106,6 +1102,75 @@ Output: } ``` +##### Conflicts + +The first variable declared in a rule head's reference divides the reference in a leading constant portion and a trailing dynamic portion. Other rules are allowed to overlap with the dynamic portion (dynamic extent) without causing a compile-time conflict. + +```rego +package example + +# R1 +p[x].r := y { + ... +} + +# R2 +p.q.r := 2 +``` + +In the above example, rule `R2` overlaps with the dynamic portion of rule `R1`'s reference (`[x].r`), which is allowed at compile-time, as these rules aren't guaranteed to produce conflicting output. +However, if `R1` defines `x` as `"q"` and `y` as, e.g. `0`, a conflict will be reported at evaluation-time. + +Conflicts are detected at compile-time, where possible, between rules even if they are within the dynamic extent of another rule. + +```rego +package example + +# R1 +p[x].r := y { + ... +} + +# R2 +p.q.r := 2 + +# R3 +p.q.r.s := 3 +``` + +Above, `R2` and `R3` are within the dynamic extent of `R1`, but are in conflict with each other, which is detected at compile-time. + +Rules are not allowed to overlap with object values of other rules. + +```rego +package example + +# R1 +p.q.r := {"s": 1} + +# R2 +p[x].r.t := 2 { + x := "q" +} +``` + +In the above example, `R1` is within the dynamic extent of `R2` and a conflict cannot be detected at compile-time. However, at evaluation-time `R2` will attempt to inject a value under key `t` in an object value defined by `R1`. This is a conflict, as rules are not allowed to modify or replace values defined by other rules. +We won't get a conflict if we update the policy to the following: + +```rego +package example + +# R1 +p.q.r.s := 1 + +# R2 +p[x].r.t := 2 { + x := "q" +} +``` + +As `R1` is now instead defining a value within the dynamic extent of `R2`'s reference, which is allowed. + ### Functions Rego supports user-defined functions that can be called with the same semantics as [Built-in Functions](#built-in-functions). They have access to both the [the data Document](../philosophy/#the-opa-document-model) and [the input Document](../philosophy/#the-opa-document-model).