Files
releases/v1/topdown/errors.go
T
Anders Eknert d0350b326e Add array.flatten built-in function (#8232)
Originally meant to be `array.concat_n`, but this name is better
as the behavior of this function differs from `array.concat` —
namely that `array.flatten` accepts any type of valued in the
input array. Only arrays are however flattened, and the rest
are appended directly to the flattened output.

Note that this function only flattens at the topmost level of
the input array — not recursively! A cursory look
at a few other languages suggest a single level is the common case.
But if others feel we should flstten more, I'm happy to make an update.

The C code for a Wasm implementstion here is cowboy coded, and
I did not manage to run the tests on my machine due to some
`docker` <-> `container` differences. I mostly just imitated
the existing code in the array category. I doubt it'll work
on the first try, but only CI can judge me.

Also:
- Remove `opa fmt` step from the Rego CI step, as this is done by
  Regal anyway a little later in the list of tasks.
- Replace some hard-coded `docker` names in the `Makefile` with `$(DOCKER)`
- Added name of built-in function missing to the unsupportedBuiltinErr
  error, as it has happened a few times now that I've used `:=` in a
  query, and had no clue what built-in it referred to.

Fixes #8226

Signed-off-by: Anders Eknert <anders.eknert@apple.com>
2026-01-27 21:46:11 +00:00

164 lines
4.2 KiB
Go

// Copyright 2017 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package topdown
import (
"errors"
"github.com/open-policy-agent/opa/v1/ast"
"github.com/open-policy-agent/opa/v1/util"
)
// Halt is a special error type that built-in function implementations return to indicate
// that policy evaluation should stop immediately.
type Halt struct {
Err error
}
func (h Halt) Error() string {
return h.Err.Error()
}
func (h Halt) Unwrap() error { return h.Err }
// Error is the error type returned by the Eval and Query functions when
// an evaluation error occurs.
type Error struct {
Code string `json:"code"`
Message string `json:"message"`
Location *ast.Location `json:"location,omitempty"`
err error `json:"-"`
}
const (
// InternalErr represents an unknown evaluation error.
InternalErr string = "eval_internal_error"
// CancelErr indicates the evaluation process was cancelled.
CancelErr string = "eval_cancel_error"
// ConflictErr indicates a conflict was encountered during evaluation. For
// instance, a conflict occurs if a rule produces multiple, differing values
// for the same key in an object. Conflict errors indicate the policy does
// not account for the data loaded into the policy engine.
ConflictErr string = "eval_conflict_error"
// TypeErr indicates evaluation stopped because an expression was applied to
// a value of an inappropriate type.
TypeErr string = "eval_type_error"
// BuiltinErr indicates a built-in function received a semantically invalid
// input or encountered some kind of runtime error, e.g., connection
// timeout, connection refused, etc.
BuiltinErr string = "eval_builtin_error"
// WithMergeErr indicates that the real and replacement data could not be merged.
WithMergeErr string = "eval_with_merge_error"
)
// IsError returns true if the err is an Error.
func IsError(err error) bool {
var e *Error
return errors.As(err, &e)
}
// IsCancel returns true if err was caused by cancellation.
func IsCancel(err error) bool {
return errors.Is(err, &Error{Code: CancelErr})
}
// Is allows matching topdown errors using errors.Is (see IsCancel).
func (e *Error) Is(target error) bool {
var t *Error
if errors.As(target, &t) {
return (t.Code == "" || e.Code == t.Code) &&
(t.Message == "" || e.Message == t.Message) &&
(t.Location == nil || t.Location.Compare(e.Location) == 0)
}
return false
}
func (e *Error) Error() string {
buf, _ := e.AppendText(make([]byte, 0, e.StringLength()))
return util.ByteSliceToString(buf)
}
func (e *Error) AppendText(buf []byte) ([]byte, error) {
if e.Location != nil {
buf, _ := e.Location.AppendText(buf)
buf = append(append(buf, ": "...), e.Code...)
buf = append(append(buf, ": "...), e.Message...)
return buf, nil
}
return append(append(append(buf, e.Code...), ": "...), e.Message...), nil
}
func (e *Error) StringLength() int {
l := len(e.Code) + 2 + len(e.Message)
if e.Location != nil {
l += e.Location.StringLength() + 2
}
return l
}
func (e *Error) Wrap(err error) *Error {
e.err = err
return e
}
func (e *Error) Unwrap() error {
return e.err
}
func functionConflictErr(loc *ast.Location) error {
return &Error{
Code: ConflictErr,
Location: loc,
Message: "functions must not produce multiple outputs for same inputs",
}
}
func completeDocConflictErr(loc *ast.Location) error {
return &Error{
Code: ConflictErr,
Location: loc,
Message: "complete rules must not produce multiple outputs",
}
}
func objectDocKeyConflictErr(loc *ast.Location) error {
return &Error{
Code: ConflictErr,
Location: loc,
Message: "object keys must be unique",
}
}
func unsupportedBuiltinErr(loc *ast.Location, name string) error {
return &Error{
Code: InternalErr,
Location: loc,
Message: "unsupported built-in: " + name,
}
}
func mergeConflictErr(loc *ast.Location) error {
return &Error{
Code: WithMergeErr,
Location: loc,
Message: "real and replacement data could not be merged",
}
}
func internalErr(loc *ast.Location, msg string) error {
return &Error{
Code: InternalErr,
Location: loc,
Message: msg,
}
}