Skip to main content

Rego Keyword: contains

Rego's contains keyword is used to incrementally build multi-value rules in a policy. Often, tasks like validation are defined as a series of checks and these break down nicely into a series of contains rules that evaluate to a larger result. A contains rule typically takes the following form:

my_rule contains value if {
# logic to check if the value should be set

# set the value
# value := ...
}

However, there some some different ways to use contains in a policy which are covered in the examples below.

note

If you're looking for the built-in function contains for substring checking, you can read about it in the built-ins section.

Examples

Building sets

While this first example is trivially simple and unlikely to be useful when building real policies, it illustrates the fundamental reason for using the contains keyword: building sets. Sets are unordered collections, and they form an important building block for many policies.

In this example, we use a multi-value rule defined using the contains keyword to create a simple list of todos. Just remember that sets are unordered and so you should not depend on the order of the result.

data.json
{}
input.json
{}
policy.rego
package play

import rego.v1

todo contains "Buy bread for lunch"

todo contains "Send off tax return"

todo contains "Do laundry"

Open in OPA Playground

RuleOutput Value
todos["Buy bread for lunch","Send off tax return","Do Laundry"]

Forming a list of error codes

Contains is used for creating a set of items, this list can be defined by policy and make reference to input or data and other rules.

policy.rego
package play

import rego.v1

errors contains "E0001" if _token_expired

errors contains "E1004" if object.get(input, "email", "") == ""

errors contains "E1209" if object.get(input, "claims", []) == []

default _token_expired := true

_token_expired if time.parse_ns("2006-01-02", input.expired_at) < time.now_ns()
input.json
{
"e-Mail": "alice@example.com",
"claims": [],
"expired_at": "2024-06-01"
}
data.json
{}

Open in OPA Playground

RuleOutput ValueNotes
errors["E0001","E1004","E1209"]The input fails all three checks.
_token_expiredtrueThis is an '_internal' rule used in setting errors

Aggregating validation failures

Using contains is commonly used to create validation messages from performing a series of checks on an object. In this example, if there are any failures then allow will be false. Using contains makes it possible to build up failures incrementally.

policy.rego
package play

import rego.v1

default allow := false

allow if count(reasons) == 0

reasons contains "name must be set" if {
object.get(input, "name", "") == ""
}

reasons contains "@example.com email blocked" if {
endswith(input.email, "@example.com")
}

reasons contains message if {
input.age < 18

message := sprintf(
"you must be %d year older",
[18 - input.age],
)
}
input.json
{
"email": "name@example.com",
"age": 17
}
data.json
{}

Open in OPA Playground

RuleOutput ValueNotes
reasons["@example.com email blocked","name must be set","you must be 1 year older"]A list of three validation messages
allowfalseBased on the number of messages in reasons

Validating lists of objects

Using contains is also useful when building up more complicated data structures in policies. In this example, contains is used to set validation errors that are grouped by the item they refer to.

policy.rego
package play

import rego.v1

approval_min := 350

reasons[item.id] contains "category must be set" if {
some item in input.items

object.get(item, "category", "") == ""
}

reasons[item.id] contains message if {
some item in input.items

item.amount > approval_min

object.get(item, "approved_by", "") == ""

message := sprintf(
"items over %d must be approved",
[approval_min],
)
}

reasons[item.id] contains "approver does not exist" if {
some item in input.items

not data.approvers[item.approved_by]
}
input.json
{
"items": [
{
"id": "1bd91655",
"name": "Lunch with James",
"amount": 57.5
},
{
"id": "226c3910",
"name": "Flight to Berlin",
"amount": 389.78,
"category": "conferences"
},
{
"id": "db5a4b23",
"name": "Recurring SaaS Subscription",
"amount": 30,
"category": "saas",
"approved_by": "90400852"
}
]
}
data.json
{
"approvers": {
"7f619663": {
"name": "Alice",
"email": "alice@example.com"
}
}
}

Open in OPA Playground

RuleOutput ValueNotes
reasons.226c3910["items over 350 must be approved"]A list of validations per item
reasons.1bd91655["category must be set"]A list of validations per item
reasons.db5a4b23["approver does not exist"]A list of validations per item
approval_min350static value set in policy code