Back to Opa

Rego

docs/docs/policy-reference/builtins/rego.mdx

1.17.12.9 KB
Original Source

<BuiltinTable category={"rego"}/>

Example

<PlaygroundExample dir={require.context("../_examples/rego/rule_metadata")} />

Metadata Merge strategies

When multiple annotations are declared along the path ancestry (chain) for a rule, how any given annotation should be selected, inherited or merged depends on the semantics of the annotation, the context of the rule, and the preferences of the developer. OPA doesn't presume what merge strategy is appropriate; instead, this lies in the hands of the developer. The following example demonstrates how some string and list type annotations in a metadata chain can be merged into a single metadata object.

rego
# METADATA
# title: My Example Package
# description: A set of rules illustrating how metadata annotations can be merged.
# authors:
# - John Doe <[email protected]>
# organizations:
# - Acme Corp.
package example

# METADATA
# scope: document
# description: A rule that merges metadata annotations in various ways.

# METADATA
# title: My Allow Rule
# authors:
# - Jane Doe <[email protected]>
allow if {
	meta := merge(rego.metadata.chain())
	meta.title == "My Allow Rule" # 'title' pulled from 'rule' scope
	meta.description == "A rule that merges metadata annotations in various ways." # 'description' pulled from 'document' scope
	meta.authors == {
		{"email": "[email protected]", "name": "Jane Doe"}, # 'authors' joined from 'package' and 'rule' scopes
		{"email": "[email protected]", "name": "John Doe"},
	}
	meta.organizations == {"Acme Corp."} # 'organizations' pulled from 'package' scope
}

allow if {
	meta := merge(rego.metadata.chain())
	meta.title == null # No 'title' present in 'rule' or 'document' scopes
	meta.description == "A rule that merges metadata annotations in various ways." # 'description' pulled from 'document' scope
	meta.authors == { # 'authors' pulled from 'package' scope
		{"email": "[email protected]", "name": "John Doe"}
	}
	meta.organizations == {"Acme Corp."} # 'organizations' pulled from 'package' scope
}

merge(chain) := meta if {
	ruleAndDoc := ["rule", "document"]
	meta := {
		"title": override_annot(chain, "title", ruleAndDoc), # looks for 'title' in 'rule' scope, then 'document' scope
		"description": override_annot(chain, "description", ruleAndDoc), # looks for 'description' in 'rule' scope, then 'document' scope
		"related_resources": override_annot(chain, "related_resources", ruleAndDoc), # looks for 'related_resources' in 'rule' scope, then 'document' scope
		"authors": merge_annot(chain, "authors"), # merges all 'authors' across all scopes
		"organizations": merge_annot(chain, "organizations"), # merges all 'organizations' across all scopes
	}
}

override_annot(chain, name, scopes) := val if {
	val := [v |
		link := chain[_]
		link.annotations.scope in scopes
		v := link.annotations[name]
	][0]
} else := null

merge_annot(chain, name) := val if {
	val := {v |
		v := chain[_].annotations[name][_]
	}
} else := null