mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
String interpolation docs (#8129)
Signed-off-by: Johan Fylling <johan.dev@fylling.se>
This commit is contained in:
@@ -5,6 +5,40 @@ project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### String Interpolation ([#4733](https://github.com/open-policy-agent/opa/issues/4733))
|
||||
|
||||
The Rego language has been extended to support [String Interpolation](https://www.openpolicyagent.org/docs/policy-language#string-interpolation),
|
||||
which provides a readable means to compose strings containing dynamic values determined at evaluation time.
|
||||
|
||||
An interpolated string is composed of a template-string containing zero or more template-expressions that evaluates to a value at evaluation time.
|
||||
The `$` character prefix identifies a template-string, and template-expressions are declared by being enclosed in curly-braces (`{`, `}`).
|
||||
|
||||
Additionally, `undefined` template-expression values don't halt evaluation; instead, `<undefined>` will be injected into the generated string.
|
||||
|
||||
```rego
|
||||
package interpolation
|
||||
|
||||
allowed_roles := ["admin", "employee"]
|
||||
|
||||
default role := "guest"
|
||||
role := input.role
|
||||
|
||||
deny contains $"User {input.username}'s role was '{role}', but must be one of {allowed_roles}" if {
|
||||
not role in allowed_roles
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
{
|
||||
"deny": [
|
||||
"User <undefined>'s role was 'guest', but must be one of [\"admin\", \"employee\"]"
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
String interpolation is a more readable and less error-prone substitute for the `sprintf` built-in function.
|
||||
|
||||
Authored by @johanfylling reported by @anderseknert
|
||||
|
||||
## 1.11.1
|
||||
|
||||
|
||||
@@ -200,6 +200,79 @@ escape special characters.
|
||||
|
||||
A simple example is a regex to match a valid Rego variable. With a regular string, the regex is `"[a-zA-Z_]\\w*"`, but with raw strings, it becomes `` `[a-zA-Z_]\w*` ``.
|
||||
|
||||
### String Interpolation
|
||||
|
||||
Runtime data can be incorporated into a string through string interpolation. An interpolated string is composed of a template-string containing zero or more template-expressions.
|
||||
The `$` character identifies a template-string, and can be used with regular double-quoted strings (`$"hello"`), and backtick-quoted raw strings (`` $`hello` ``).
|
||||
|
||||
A template-expression is enclosed in curly-braces (`{`,`}`), and must contain a single expression that evaluate to a value, e.g.:
|
||||
|
||||
- Primitive values: `$"{1} {2.3} {"foo"} {false} {null}"`
|
||||
- Composite values: `$"{[true, false]} {{1, 2}} {{"a": "b"}}"`
|
||||
- Variables: `x := "foo"; a := "$"{x}"`
|
||||
- References: `"$"{input.x} {data.y}"`
|
||||
- Function calls: `$"{abs(-1)} {1 + 2}"`
|
||||
- Comprehensions: `$"{[x | ...]} {{x | ...}} {{x: y | ...}}"`
|
||||
|
||||
```rego
|
||||
package interpolation
|
||||
|
||||
username := "Alice"
|
||||
|
||||
a := $"Hello {username}!"
|
||||
```
|
||||
|
||||
<RunSnippet command="data.interpolation.a"/>
|
||||
|
||||
#### Undefined values
|
||||
|
||||
If a template-expression evaluates to an `undefined` value,
|
||||
the string `"<undefined>"` will be emitted instead. This means string interpolation is safe to use in cases where a string result is
|
||||
always expected, but not all expression values are guaranteed at evaluation time.
|
||||
|
||||
```rego
|
||||
package interpolation
|
||||
|
||||
default role := "guest"
|
||||
role := input.role
|
||||
allowed_roles := ["admin", "employee"]
|
||||
|
||||
default location := "unknown"
|
||||
location := input.location
|
||||
allowed_locations := ["Narnia", "Mordor"]
|
||||
|
||||
deny contains $"User {input.username}'s role was '{role}', but must be one of {allowed_roles}" if {
|
||||
not role in allowed_roles
|
||||
}
|
||||
|
||||
deny contains sprintf("User %s's location was '%s', but must be one of %v", [input.username, location, allowed_locations]) if {
|
||||
not location in allowed_locations
|
||||
}
|
||||
```
|
||||
|
||||
<RunSnippet command="data.interpolation.deny"/>
|
||||
|
||||
In the above example, the `input.username` value is `undefined`; notice how
|
||||
|
||||
- the first `deny` rule uses string interpolation, and will output `User <undefined>'s role was 'guest', but must be one of ["admin", "employee"]`, whereas
|
||||
- the second `deny` rule uses `sprintf`, and will output no result as it failed to evaluate even though `input.username` is inconsequential to the logic in the rule's body.
|
||||
|
||||
Compared to the `sprintf` [built-in function](#built-in-functions), not halting evaluation on `undefined` values make interpolated strings less error-prone, and is therefore the recommended alternative.
|
||||
|
||||
#### Escaping
|
||||
|
||||
Since the left curly-brace (`{`) is reserved for starting a template-expression within a template-string, this character can be escaped with a backslash (`\`) in cases where a template expression is not wanted:
|
||||
|
||||
```rego
|
||||
package interpolation
|
||||
|
||||
a := $"In this template-string, \{ will not start a template-expression."
|
||||
```
|
||||
|
||||
<RunSnippet command="data.interpolation.a"/>
|
||||
|
||||
Left curly-brace escaping is also present for multi-line raw template-strings (`` $`\{}` ``), differentiating them from regular raw strings, where no escaping is recognized.
|
||||
|
||||
## Composite Values
|
||||
|
||||
Composite values define collections. In simple cases, composite values can be treated as constants like [scalar values](#scalar-values):
|
||||
|
||||
@@ -419,7 +419,9 @@ ref-arg-brack = "[" ( scalar | var | array | object | set | "_" ) "]"
|
||||
ref-arg-dot = "." var
|
||||
var = ( ALPHA | "_" ) { ALPHA | DIGIT | "_" }
|
||||
scalar = string | NUMBER | TRUE | FALSE | NULL
|
||||
string = STRING | raw-string
|
||||
string = STRING | raw-string | template-string
|
||||
template-string = "$" ( '"' { CHAR-'"' | template-expr } '"' | "`" { CHAR-"`" | template-expr } "`" )
|
||||
template-expr = "{" ( ref | var | scalar | array | object | set | array-compr | object-compr | set-compr | expr-call | expr-infix | expr-parens | unary-expr ) "}"
|
||||
raw-string = "`" { CHAR-"`" } "`"
|
||||
array = "[" term { "," term } "]"
|
||||
object = "{" object-item { "," object-item } "}"
|
||||
|
||||
Reference in New Issue
Block a user