From eacd5b88b1699489ef07b24bbdca358fc72e47db Mon Sep 17 00:00:00 2001 From: Ansh Rai Date: Tue, 14 Jul 2026 16:02:44 +0530 Subject: [PATCH] Fix missing/incorrect @OpaBuiltin annotations for union and object.union Signed-off-by: Ansh Rai --- .../opa/ast/builtin/impls/ObjectBuiltins.java | 28 ++++++++++++++----- .../opa/ast/builtin/impls/SetBuiltins.java | 19 ++++++++++++- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/ObjectBuiltins.java b/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/ObjectBuiltins.java index c7c1a44a..01755abb 100644 --- a/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/ObjectBuiltins.java +++ b/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/ObjectBuiltins.java @@ -65,20 +65,34 @@ public RegoObject unionN(EvaluationContext ctx, RegoValue[] args) { } @OpaBuiltin( - name = "object.keys", - description = "Returns a set of keys in the supplied object.", + name = "object.union", + description = + "Creates a new object of the asymmetric union of two objects. For example: " + + "`object.union({\"a\": 1}, {\"b\": 2})` results in `{\"a\": 1, \"b\": 2}`. " + + "If both objects have the same key and both values are objects, the values are " + + "merged recursively. Otherwise, the value in the right-hand object `b` wins.", + categories = {"objects"}, args = { @OpaType( type = "object", - name = "object", - description = "object to get keys from", + name = "a", + description = "left-hand object", + dynamic = @OpaDynamic(keyType = "any", valueType = "any")), + @OpaType( + type = "object", + name = "b", + description = "right-hand object", dynamic = @OpaDynamic(keyType = "any", valueType = "any")) }, result = @OpaType( - type = "set", - description = "set of keys in `object`", - dynamic = @OpaDynamic(type = "any"))) + type = "object", + name = "output", + dynamic = @OpaDynamic(keyType = "any", valueType = "any"), + description = + "a new object which is the result of an asymmetric recursive union of two " + + "objects where conflicts are resolved by choosing the key from the " + + "right-hand object `b`")) public RegoObject union(EvaluationContext ctx, RegoValue[] args) { if (!(args[0] instanceof RegoObject)) { throw new TypeError("object.union: operand 1 must be object but got " + args[0].getTypeName()); diff --git a/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/SetBuiltins.java b/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/SetBuiltins.java index 2b28ecf6..7aa25533 100644 --- a/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/SetBuiltins.java +++ b/opa-evaluator/src/main/java/io/github/open_policy_agent/opa/ast/builtin/impls/SetBuiltins.java @@ -26,8 +26,25 @@ public static Map> "union", instance::union); } + @OpaBuiltin( + name = "union", + description = "Returns the union of the given input sets.", + categories = {"sets"}, + args = { + @OpaType( + type = "set", + name = "xs", + description = "set of sets to merge", + dynamic = @OpaDynamic(type = "set")) + }, + result = + @OpaType( + type = "set", + name = "y", + description = "the union of all `xs` sets", + dynamic = @OpaDynamic(type = "any"))) public RegoSet union(EvaluationContext ctx, RegoValue[] args) { - RegoSet xs = (RegoSet) args[0]; + RegoSet xs = getArg(args, 0, RegoSet.class); Set y = xs.getValue().stream()