From 4ea9debe05c17da06a3f900c7915c31140f5116a Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Fri, 13 Mar 2026 10:24:13 -0400 Subject: [PATCH 01/10] dont require namespace on list --- docs/grpc/index.html | 4 +- .../policy/actions/actions.openapi.yaml | 4 +- protocol/go/policy/actions/actions.pb.go | 314 +++++++++--------- service/integration/actions_test.go | 35 ++ service/policy/actions/actions.proto | 13 +- service/policy/actions/actions_test.go | 16 +- service/policy/db/actions.sql.go | 68 ++-- service/policy/db/queries/actions.sql | 22 +- 8 files changed, 275 insertions(+), 201 deletions(-) diff --git a/docs/grpc/index.html b/docs/grpc/index.html index 3aebde5638..bb0bc3e41d 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -7627,14 +7627,14 @@

ListActionsRequest

namespace_id string -

ID of the namespace. Required if namespace_fqn is not provided.

+

ID of the namespace to scope results. If omitted, returns actions across namespaces.

namespace_fqn string -

FQN of the namespace. Required if namespace_id is not provided.

+

FQN of the namespace to scope results. If omitted, returns actions across namespaces.

diff --git a/docs/openapi/policy/actions/actions.openapi.yaml b/docs/openapi/policy/actions/actions.openapi.yaml index c7189a2bfb..70fdd4d068 100644 --- a/docs/openapi/policy/actions/actions.openapi.yaml +++ b/docs/openapi/policy/actions/actions.openapi.yaml @@ -1172,13 +1172,13 @@ components: type: string title: namespace_id format: uuid - description: ID of the namespace. Required if namespace_fqn is not provided. + description: ID of the namespace to scope results. If omitted, returns actions across namespaces. namespaceFqn: type: string title: namespace_fqn minLength: 1 format: uri - description: FQN of the namespace. Required if namespace_id is not provided. + description: FQN of the namespace to scope results. If omitted, returns actions across namespaces. pagination: title: pagination description: Optional diff --git a/protocol/go/policy/actions/actions.pb.go b/protocol/go/policy/actions/actions.pb.go index d6d89c407c..9cada6c683 100644 --- a/protocol/go/policy/actions/actions.pb.go +++ b/protocol/go/policy/actions/actions.pb.go @@ -185,9 +185,9 @@ type ListActionsRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // ID of the namespace. Required if namespace_fqn is not provided. + // ID of the namespace to scope results. If omitted, returns actions across namespaces. NamespaceId string `protobuf:"bytes,1,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - // FQN of the namespace. Required if namespace_id is not provided. + // FQN of the namespace to scope results. If omitted, returns actions across namespaces. NamespaceFqn string `protobuf:"bytes,2,opt,name=namespace_fqn,json=namespaceFqn,proto3" json:"namespace_fqn,omitempty"` // Optional Pagination *policy.PageRequest `protobuf:"bytes,10,opt,name=pagination,proto3" json:"pagination,omitempty"` @@ -664,7 +664,7 @@ var file_policy_actions_actions_proto_rawDesc = []byte{ 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x16, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x73, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x94, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb5, 0x05, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, @@ -692,8 +692,8 @@ var file_policy_actions_actions_proto_rawDesc = []byte{ 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xba, 0x48, 0x0a, 0xd8, 0x01, 0x01, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x0c, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, 0x71, 0x6e, 0x3a, 0xc2, 0x01, 0xba, 0x48, - 0xbe, 0x01, 0x1a, 0xbb, 0x01, 0x0a, 0x1b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, 0x71, 0x6e, 0x3a, 0xe3, 0x01, 0xba, 0x48, + 0xdf, 0x01, 0x1a, 0xbb, 0x01, 0x0a, 0x1b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x54, 0x45, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, @@ -705,160 +705,162 @@ var file_policy_actions_actions_proto_rawDesc = []byte{ 0x73, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, - 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, - 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x7e, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x61, - 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x61, 0x70, - 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x61, 0x70, - 0x70, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xcd, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x0c, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x0c, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, 0x71, 0x6e, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, - 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, - 0x24, 0xba, 0x48, 0x21, 0x22, 0x1f, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x5f, 0x69, 0x64, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, - 0x66, 0x71, 0x6e, 0x10, 0x01, 0x22, 0xbd, 0x01, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, - 0x10, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, - 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x12, 0x35, 0x0a, 0x0e, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x12, - 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xf7, 0x03, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xa6, 0x02, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x91, 0x02, 0xba, - 0x48, 0x8d, 0x02, 0xba, 0x01, 0x81, 0x02, 0x0a, 0x12, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xad, 0x01, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, - 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, - 0x63, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x69, - 0x6e, 0x67, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x75, - 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x62, 0x75, 0x74, 0x20, 0x6e, - 0x6f, 0x74, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x20, - 0x6f, 0x72, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, - 0x72, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x61, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, 0x62, - 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, - 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x1a, 0x3b, 0x74, 0x68, 0x69, - 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, - 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, - 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, - 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0xc8, 0x01, 0x01, 0x72, 0x03, 0x18, 0xfd, 0x01, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, - 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x66, 0x71, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, - 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x46, 0x71, 0x6e, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x24, 0xba, 0x48, 0x21, 0x22, 0x1f, - 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x0a, 0x0d, - 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x10, 0x01, 0x22, - 0x3e, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, - 0xf3, 0x03, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, - 0x64, 0x12, 0xb6, 0x02, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x42, 0xa1, 0x02, 0xba, 0x48, 0x9d, 0x02, 0xba, 0x01, 0x94, 0x02, 0x0a, 0x12, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, - 0xad, 0x01, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x75, - 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, - 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, - 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x20, 0x61, - 0x6e, 0x64, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x62, - 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, - 0x72, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, - 0x61, 0x63, 0x74, 0x65, 0x72, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, - 0x64, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x77, 0x69, - 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, - 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x1a, - 0x4e, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3d, 0x3d, 0x20, 0x30, - 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, - 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, - 0x3a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, - 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0x72, - 0x03, 0x18, 0xfd, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, - 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x54, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x65, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x16, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x65, 0x68, - 0x61, 0x76, 0x69, 0x6f, 0x72, 0x22, 0x3e, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, - 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2f, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, - 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3e, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x22, 0x1f, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x10, + 0x00, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, + 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x7e, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x10, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x6d, + 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x61, + 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0f, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x61, + 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xcd, 0x01, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, + 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0d, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x0c, 0x6e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, 0x71, 0x6e, 0x12, 0x33, 0x0a, 0x0a, 0x70, + 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x3a, 0x24, 0xba, 0x48, 0x21, 0x22, 0x1f, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x5f, 0x66, 0x71, 0x6e, 0x10, 0x00, 0x22, 0xbd, 0x01, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, + 0x0a, 0x10, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, + 0x72, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x12, 0x35, 0x0a, 0x0e, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, + 0x12, 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, + 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xf7, 0x03, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xa6, + 0x02, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x91, 0x02, + 0xba, 0x48, 0x8d, 0x02, 0xba, 0x01, 0x81, 0x02, 0x0a, 0x12, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0xad, 0x01, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, + 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, 0x75, 0x6d, 0x65, 0x72, + 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x69, 0x6e, 0x67, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, + 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x20, 0x62, 0x75, 0x74, 0x20, + 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, + 0x20, 0x6f, 0x72, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, + 0x65, 0x72, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x20, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20, + 0x62, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x20, 0x74, 0x6f, + 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, 0x1a, 0x3b, 0x74, 0x68, + 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, + 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, 0x3f, 0x3a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, + 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, + 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, 0xc8, 0x01, 0x01, 0x72, 0x03, 0x18, 0xfd, + 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, + 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, + 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x46, 0x71, 0x6e, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x24, 0xba, 0x48, 0x21, 0x22, + 0x1f, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x0a, + 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x10, 0x01, + 0x22, 0x3e, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0xf3, 0x03, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, + 0x69, 0x64, 0x12, 0xb6, 0x02, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0xa1, 0x02, 0xba, 0x48, 0x9d, 0x02, 0xba, 0x01, 0x94, 0x02, 0x0a, 0x12, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, + 0x12, 0xad, 0x01, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6d, + 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x6e, + 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x61, + 0x6c, 0x6c, 0x6f, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x79, 0x70, 0x68, 0x65, 0x6e, 0x73, 0x20, + 0x61, 0x6e, 0x64, 0x20, 0x75, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x73, 0x20, + 0x62, 0x75, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x20, 0x6f, 0x72, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x20, 0x63, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x73, 0x74, 0x6f, 0x72, + 0x65, 0x64, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x77, + 0x69, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, + 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x20, 0x63, 0x61, 0x73, 0x65, 0x2e, + 0x1a, 0x4e, 0x73, 0x69, 0x7a, 0x65, 0x28, 0x74, 0x68, 0x69, 0x73, 0x29, 0x20, 0x3d, 0x3d, 0x20, + 0x30, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, + 0x73, 0x28, 0x27, 0x5e, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x28, + 0x3f, 0x3a, 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5f, 0x2d, 0x5d, 0x2a, + 0x5b, 0x61, 0x2d, 0x7a, 0x41, 0x2d, 0x5a, 0x30, 0x2d, 0x39, 0x5d, 0x29, 0x3f, 0x24, 0x27, 0x29, + 0x72, 0x03, 0x18, 0xfd, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, + 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x54, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x65, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x16, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x65, + 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x22, 0x3e, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0xd4, 0x03, 0x0a, 0x0d, 0x41, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x41, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x0b, - 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x23, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, - 0x12, 0x5b, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x23, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xb3, 0x01, - 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x0c, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, - 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xa2, 0x02, 0x03, 0x50, - 0x41, 0x58, 0xaa, 0x02, 0x0e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0xca, 0x02, 0x0e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x41, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0xe2, 0x02, 0x1a, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x41, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0xea, 0x02, 0x0f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x3a, 0x3a, 0x41, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x2f, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, + 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3e, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0xd4, 0x03, 0x0a, 0x0d, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x09, 0x47, 0x65, 0x74, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, + 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x22, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x5b, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x23, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xb3, + 0x01, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x0c, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xa2, 0x02, 0x03, + 0x50, 0x41, 0x58, 0xaa, 0x02, 0x0e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0xca, 0x02, 0x0e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0xe2, 0x02, 0x1a, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0xea, 0x02, 0x0f, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x3a, 0x3a, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/service/integration/actions_test.go b/service/integration/actions_test.go index 456aec3fd9..b844cfbb11 100644 --- a/service/integration/actions_test.go +++ b/service/integration/actions_test.go @@ -187,6 +187,41 @@ func (s *ActionsSuite) Test_ListActions_FiltersCustomActionsByNamespace_Succeeds s.False(foundSecond) } +func (s *ActionsSuite) Test_ListActions_WithoutNamespace_ReturnsAcrossNamespaces_Succeeds() { + name := fmt.Sprintf("global-list-action-%d", time.Now().UnixNano()) + + inDefault, err := s.db.PolicyClient.CreateAction(s.ctx, &actions.CreateActionRequest{ + Name: name, + NamespaceId: s.defaultNamespaceID(), + }) + s.Require().NoError(err) + + inOther, err := s.db.PolicyClient.CreateAction(s.ctx, &actions.CreateActionRequest{ + Name: name, + NamespaceId: s.otherNamespaceID(), + }) + s.Require().NoError(err) + + list, err := s.db.PolicyClient.ListActions(s.ctx, &actions.ListActionsRequest{}) + s.Require().NoError(err) + + foundDefault := false + foundOther := false + for _, action := range list.GetActionsCustom() { + if action.GetId() == inDefault.GetId() { + foundDefault = true + s.Equal(s.defaultNamespaceID(), action.GetNamespace().GetId()) + } + if action.GetId() == inOther.GetId() { + foundOther = true + s.Equal(s.otherNamespaceID(), action.GetNamespace().GetId()) + } + } + + s.True(foundDefault) + s.True(foundOther) +} + func (s *ActionsSuite) Test_ListActions_LegacyCustomAction_NamespaceProjection_Succeeds() { legacy := s.f.GetCustomActionKey("custom_action_1") assertLegacyProjected := func(list *actions.ListActionsResponse) { diff --git a/service/policy/actions/actions.proto b/service/policy/actions/actions.proto index 7ae91a9b6a..8a8d882f26 100644 --- a/service/policy/actions/actions.proto +++ b/service/policy/actions/actions.proto @@ -12,6 +12,7 @@ import "policy/selectors.proto"; */ message GetActionRequest { + option (buf.validate.message).oneof = { fields: ["namespace_id", "namespace_fqn"], required: false }; option (buf.validate.message).cel = { id: "namespace_required_for_name" message: "Either namespace_id or namespace_fqn must be provided when getting an action by name" @@ -54,12 +55,14 @@ message GetActionResponse { } message ListActionsRequest { - // Required - option (buf.validate.message).oneof = { fields: ["namespace_id", "namespace_fqn"], required: true }; + // Optional + option (buf.validate.message).oneof = { fields: ["namespace_id", "namespace_fqn"], required: false }; - // ID of the namespace. Required if namespace_fqn is not provided. - string namespace_id = 1 [(buf.validate.field).string.uuid = true]; - // FQN of the namespace. Required if namespace_id is not provided. + // ID of the namespace to scope results. If omitted, returns actions across namespaces. + string namespace_id = 1 [ + (buf.validate.field).string.uuid = true + ]; + // FQN of the namespace to scope results. If omitted, returns actions across namespaces. string namespace_fqn = 2 [ (buf.validate.field).string = { min_len : 1 diff --git a/service/policy/actions/actions_test.go b/service/policy/actions/actions_test.go index 354321b5f1..87429d93ff 100644 --- a/service/policy/actions/actions_test.go +++ b/service/policy/actions/actions_test.go @@ -240,13 +240,17 @@ func (s *ActionSuite) Test_GetAction_Fails() { } func (s *ActionSuite) Test_ListActions_Succeeds() { + reqNoNamespace := &actions.ListActionsRequest{} + err := s.v.Validate(reqNoNamespace) + s.Require().NoError(err) + reqPaginated := &actions.ListActionsRequest{ NamespaceId: validUUID, Pagination: &policy.PageRequest{ Limit: 1, }, } - err := s.v.Validate(reqPaginated) + err = s.v.Validate(reqPaginated) s.Require().NoError(err) reqPaginated.Pagination.Offset = 100 @@ -259,15 +263,9 @@ func (s *ActionSuite) Test_ListActions_Succeeds() { } func (s *ActionSuite) Test_ListActions_Fails() { - // missing namespace - req := &actions.ListActionsRequest{} - err := s.v.Validate(req) - s.Require().Error(err) - s.Require().Contains(err.Error(), errMessageOneof) - // invalid namespace id - req = &actions.ListActionsRequest{NamespaceId: "invalid-uuid"} - err = s.v.Validate(req) + req := &actions.ListActionsRequest{NamespaceId: "invalid-uuid"} + err := s.v.Validate(req) s.Require().Error(err) s.Require().Contains(err.Error(), errMessageUUID) diff --git a/service/policy/db/actions.sql.go b/service/policy/db/actions.sql.go index 9972a65314..0590910e91 100644 --- a/service/policy/db/actions.sql.go +++ b/service/policy/db/actions.sql.go @@ -369,16 +369,20 @@ WITH resolved_namespace AS ( FROM attribute_namespaces n LEFT JOIN attribute_fqns fqns ON fqns.namespace_id = n.id AND fqns.attribute_id IS NULL AND fqns.value_id IS NULL WHERE - ($3::uuid IS NOT NULL AND n.id = $3::uuid) + ($1::uuid IS NOT NULL AND n.id = $1::uuid) OR - ($4::text IS NOT NULL AND fqns.fqn = $4::text) + ($2::text IS NOT NULL AND fqns.fqn = $2::text) LIMIT 1 ), counted AS ( SELECT COUNT(a.id) AS total FROM actions a - JOIN resolved_namespace rn ON TRUE - WHERE a.is_standard = TRUE OR a.namespace_id = rn.id OR a.namespace_id IS NULL + LEFT JOIN resolved_namespace rn ON TRUE + WHERE + rn.id IS NULL + OR a.is_standard = TRUE + OR a.namespace_id = rn.id + OR a.namespace_id IS NULL ) SELECT a.id, @@ -390,11 +394,12 @@ SELECT )) as metadata, a.is_standard, CASE - WHEN a.namespace_id IS NULL THEN JSON_BUILD_OBJECT( + WHEN a.namespace_id IS NULL AND rn.id IS NOT NULL THEN JSON_BUILD_OBJECT( 'id', rn.id, 'name', rn.name, 'fqn', rn.fqn ) + WHEN a.namespace_id IS NULL THEN NULL ELSE JSON_BUILD_OBJECT( 'id', n.id, 'name', n.name, @@ -403,21 +408,28 @@ SELECT END AS namespace, counted.total FROM actions a -JOIN resolved_namespace rn ON TRUE +LEFT JOIN resolved_namespace rn ON TRUE LEFT JOIN attribute_namespaces n ON a.namespace_id = n.id LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL CROSS JOIN counted -WHERE a.is_standard = TRUE OR a.namespace_id = rn.id OR a.namespace_id IS NULL +WHERE + ( + $1::uuid IS NULL + AND $2::text IS NULL + ) + OR a.is_standard = TRUE + OR a.namespace_id = rn.id + OR a.namespace_id IS NULL ORDER BY a.created_at DESC -LIMIT $2 -OFFSET $1 +LIMIT $4 +OFFSET $3 ` type listActionsParams struct { - Offset int32 `json:"offset_"` - Limit int32 `json:"limit_"` NamespaceID pgtype.UUID `json:"namespace_id"` NamespaceFqn pgtype.Text `json:"namespace_fqn"` + Offset int32 `json:"offset_"` + Limit int32 `json:"limit_"` } type listActionsRow struct { @@ -441,16 +453,20 @@ type listActionsRow struct { // FROM attribute_namespaces n // LEFT JOIN attribute_fqns fqns ON fqns.namespace_id = n.id AND fqns.attribute_id IS NULL AND fqns.value_id IS NULL // WHERE -// ($3::uuid IS NOT NULL AND n.id = $3::uuid) +// ($1::uuid IS NOT NULL AND n.id = $1::uuid) // OR -// ($4::text IS NOT NULL AND fqns.fqn = $4::text) +// ($2::text IS NOT NULL AND fqns.fqn = $2::text) // LIMIT 1 // ), // counted AS ( // SELECT COUNT(a.id) AS total // FROM actions a -// JOIN resolved_namespace rn ON TRUE -// WHERE a.is_standard = TRUE OR a.namespace_id = rn.id OR a.namespace_id IS NULL +// LEFT JOIN resolved_namespace rn ON TRUE +// WHERE +// rn.id IS NULL +// OR a.is_standard = TRUE +// OR a.namespace_id = rn.id +// OR a.namespace_id IS NULL // ) // SELECT // a.id, @@ -462,11 +478,12 @@ type listActionsRow struct { // )) as metadata, // a.is_standard, // CASE -// WHEN a.namespace_id IS NULL THEN JSON_BUILD_OBJECT( +// WHEN a.namespace_id IS NULL AND rn.id IS NOT NULL THEN JSON_BUILD_OBJECT( // 'id', rn.id, // 'name', rn.name, // 'fqn', rn.fqn // ) +// WHEN a.namespace_id IS NULL THEN NULL // ELSE JSON_BUILD_OBJECT( // 'id', n.id, // 'name', n.name, @@ -475,20 +492,27 @@ type listActionsRow struct { // END AS namespace, // counted.total // FROM actions a -// JOIN resolved_namespace rn ON TRUE +// LEFT JOIN resolved_namespace rn ON TRUE // LEFT JOIN attribute_namespaces n ON a.namespace_id = n.id // LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL // CROSS JOIN counted -// WHERE a.is_standard = TRUE OR a.namespace_id = rn.id OR a.namespace_id IS NULL +// WHERE +// ( +// $1::uuid IS NULL +// AND $2::text IS NULL +// ) +// OR a.is_standard = TRUE +// OR a.namespace_id = rn.id +// OR a.namespace_id IS NULL // ORDER BY a.created_at DESC -// LIMIT $2 -// OFFSET $1 +// LIMIT $4 +// OFFSET $3 func (q *Queries) listActions(ctx context.Context, arg listActionsParams) ([]listActionsRow, error) { rows, err := q.db.Query(ctx, listActions, - arg.Offset, - arg.Limit, arg.NamespaceID, arg.NamespaceFqn, + arg.Offset, + arg.Limit, ) if err != nil { return nil, err diff --git a/service/policy/db/queries/actions.sql b/service/policy/db/queries/actions.sql index 7d4744cf20..46c3d93810 100644 --- a/service/policy/db/queries/actions.sql +++ b/service/policy/db/queries/actions.sql @@ -19,8 +19,12 @@ WITH resolved_namespace AS ( counted AS ( SELECT COUNT(a.id) AS total FROM actions a - JOIN resolved_namespace rn ON TRUE - WHERE a.is_standard = TRUE OR a.namespace_id = rn.id OR a.namespace_id IS NULL + LEFT JOIN resolved_namespace rn ON TRUE + WHERE + rn.id IS NULL + OR a.is_standard = TRUE + OR a.namespace_id = rn.id + OR a.namespace_id IS NULL ) SELECT a.id, @@ -32,11 +36,12 @@ SELECT )) as metadata, a.is_standard, CASE - WHEN a.namespace_id IS NULL THEN JSON_BUILD_OBJECT( + WHEN a.namespace_id IS NULL AND rn.id IS NOT NULL THEN JSON_BUILD_OBJECT( 'id', rn.id, 'name', rn.name, 'fqn', rn.fqn ) + WHEN a.namespace_id IS NULL THEN NULL ELSE JSON_BUILD_OBJECT( 'id', n.id, 'name', n.name, @@ -45,11 +50,18 @@ SELECT END AS namespace, counted.total FROM actions a -JOIN resolved_namespace rn ON TRUE +LEFT JOIN resolved_namespace rn ON TRUE LEFT JOIN attribute_namespaces n ON a.namespace_id = n.id LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL CROSS JOIN counted -WHERE a.is_standard = TRUE OR a.namespace_id = rn.id OR a.namespace_id IS NULL +WHERE + ( + sqlc.narg('namespace_id')::uuid IS NULL + AND sqlc.narg('namespace_fqn')::text IS NULL + ) + OR a.is_standard = TRUE + OR a.namespace_id = rn.id + OR a.namespace_id IS NULL ORDER BY a.created_at DESC LIMIT @limit_ OFFSET @offset_; From 63c70c7a4bd7a4a3d3867329f37ac733fbb150fc Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Fri, 13 Mar 2026 10:43:23 -0400 Subject: [PATCH 02/10] fix query --- service/policy/db/actions.sql.go | 26 ++------------------------ service/policy/db/queries/actions.sql | 13 +------------ 2 files changed, 3 insertions(+), 36 deletions(-) diff --git a/service/policy/db/actions.sql.go b/service/policy/db/actions.sql.go index 0590910e91..767a6f9948 100644 --- a/service/policy/db/actions.sql.go +++ b/service/policy/db/actions.sql.go @@ -373,16 +373,6 @@ WITH resolved_namespace AS ( OR ($2::text IS NOT NULL AND fqns.fqn = $2::text) LIMIT 1 -), -counted AS ( - SELECT COUNT(a.id) AS total - FROM actions a - LEFT JOIN resolved_namespace rn ON TRUE - WHERE - rn.id IS NULL - OR a.is_standard = TRUE - OR a.namespace_id = rn.id - OR a.namespace_id IS NULL ) SELECT a.id, @@ -406,12 +396,11 @@ SELECT 'fqn', ns_fqns.fqn ) END AS namespace, - counted.total + COUNT(*) OVER() as total FROM actions a LEFT JOIN resolved_namespace rn ON TRUE LEFT JOIN attribute_namespaces n ON a.namespace_id = n.id LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL -CROSS JOIN counted WHERE ( $1::uuid IS NULL @@ -457,16 +446,6 @@ type listActionsRow struct { // OR // ($2::text IS NOT NULL AND fqns.fqn = $2::text) // LIMIT 1 -// ), -// counted AS ( -// SELECT COUNT(a.id) AS total -// FROM actions a -// LEFT JOIN resolved_namespace rn ON TRUE -// WHERE -// rn.id IS NULL -// OR a.is_standard = TRUE -// OR a.namespace_id = rn.id -// OR a.namespace_id IS NULL // ) // SELECT // a.id, @@ -490,12 +469,11 @@ type listActionsRow struct { // 'fqn', ns_fqns.fqn // ) // END AS namespace, -// counted.total +// COUNT(*) OVER() as total // FROM actions a // LEFT JOIN resolved_namespace rn ON TRUE // LEFT JOIN attribute_namespaces n ON a.namespace_id = n.id // LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL -// CROSS JOIN counted // WHERE // ( // $1::uuid IS NULL diff --git a/service/policy/db/queries/actions.sql b/service/policy/db/queries/actions.sql index 46c3d93810..ed41517ab5 100644 --- a/service/policy/db/queries/actions.sql +++ b/service/policy/db/queries/actions.sql @@ -15,16 +15,6 @@ WITH resolved_namespace AS ( OR (sqlc.narg('namespace_fqn')::text IS NOT NULL AND fqns.fqn = sqlc.narg('namespace_fqn')::text) LIMIT 1 -), -counted AS ( - SELECT COUNT(a.id) AS total - FROM actions a - LEFT JOIN resolved_namespace rn ON TRUE - WHERE - rn.id IS NULL - OR a.is_standard = TRUE - OR a.namespace_id = rn.id - OR a.namespace_id IS NULL ) SELECT a.id, @@ -48,12 +38,11 @@ SELECT 'fqn', ns_fqns.fqn ) END AS namespace, - counted.total + COUNT(*) OVER() as total FROM actions a LEFT JOIN resolved_namespace rn ON TRUE LEFT JOIN attribute_namespaces n ON a.namespace_id = n.id LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attribute_id IS NULL AND ns_fqns.value_id IS NULL -CROSS JOIN counted WHERE ( sqlc.narg('namespace_id')::uuid IS NULL From e88337b806827cc224c5dcc03c7be55be77a0cb3 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Fri, 13 Mar 2026 14:28:08 -0400 Subject: [PATCH 03/10] dont populate namespace field --- service/integration/actions_test.go | 14 +++++------ service/policy/db/actions.sql.go | 36 ++++++--------------------- service/policy/db/queries/actions.sql | 10 -------- 3 files changed, 14 insertions(+), 46 deletions(-) diff --git a/service/integration/actions_test.go b/service/integration/actions_test.go index b844cfbb11..6f50c20d18 100644 --- a/service/integration/actions_test.go +++ b/service/integration/actions_test.go @@ -222,16 +222,15 @@ func (s *ActionsSuite) Test_ListActions_WithoutNamespace_ReturnsAcrossNamespaces s.True(foundOther) } -func (s *ActionsSuite) Test_ListActions_LegacyCustomAction_NamespaceProjection_Succeeds() { +func (s *ActionsSuite) Test_ListActions_LegacyCustomAction_NamespaceUnset_Succeeds() { legacy := s.f.GetCustomActionKey("custom_action_1") - assertLegacyProjected := func(list *actions.ListActionsResponse) { + assertLegacyUnset := func(list *actions.ListActionsResponse) { s.T().Helper() found := false for _, action := range list.GetActionsCustom() { if action.GetId() == legacy.ID { found = true - s.Equal(s.defaultNamespaceID(), action.GetNamespace().GetId()) - s.Equal(s.defaultNamespaceFQN(), action.GetNamespace().GetFqn()) + s.Nil(action.GetNamespace()) } } s.True(found) @@ -239,11 +238,11 @@ func (s *ActionsSuite) Test_ListActions_LegacyCustomAction_NamespaceProjection_S listByID, err := s.db.PolicyClient.ListActions(s.ctx, &actions.ListActionsRequest{NamespaceId: s.defaultNamespaceID()}) s.Require().NoError(err) - assertLegacyProjected(listByID) + assertLegacyUnset(listByID) listByFQN, err := s.db.PolicyClient.ListActions(s.ctx, &actions.ListActionsRequest{NamespaceFqn: s.defaultNamespaceFQN()}) s.Require().NoError(err) - assertLegacyProjected(listByFQN) + assertLegacyUnset(listByFQN) } func (s *ActionsSuite) Test_GetAction_Id_Succeeds() { @@ -339,8 +338,7 @@ func (s *ActionsSuite) Test_GetAction_Name_LegacyCustomAction_Succeeds() { assertLegacyGet := func(action *policy.Action) { s.T().Helper() s.Equal(legacy.ID, action.GetId()) - s.Equal(s.defaultNamespaceID(), action.GetNamespace().GetId()) - s.Equal(s.defaultNamespaceFQN(), action.GetNamespace().GetFqn()) + s.Nil(action.GetNamespace()) } byID, err := s.db.PolicyClient.GetAction(s.ctx, &actions.GetActionRequest{ diff --git a/service/policy/db/actions.sql.go b/service/policy/db/actions.sql.go index 767a6f9948..54f95acef7 100644 --- a/service/policy/db/actions.sql.go +++ b/service/policy/db/actions.sql.go @@ -227,11 +227,6 @@ SELECT a.is_standard, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', a.metadata -> 'labels', 'created_at', a.created_at, 'updated_at', a.updated_at)) AS metadata, CASE - WHEN a.namespace_id IS NULL AND $1::text IS NOT NULL THEN JSON_BUILD_OBJECT( - 'id', rn.id, - 'name', rn.name, - 'fqn', rn.fqn - ) WHEN a.namespace_id IS NULL THEN NULL ELSE JSON_BUILD_OBJECT( 'id', n.id, @@ -245,11 +240,11 @@ LEFT JOIN attribute_fqns ns_fqns ON ns_fqns.namespace_id = n.id AND ns_fqns.attr LEFT JOIN resolved_namespace rn ON TRUE WHERE ( - ($2::uuid IS NOT NULL AND a.id = $2::uuid) + ($1::uuid IS NOT NULL AND a.id = $1::uuid) OR ( - $1::text IS NOT NULL - AND a.name = $1::text + $2::text IS NOT NULL + AND a.name = $2::text AND ( (rn.id IS NOT NULL AND (a.namespace_id = rn.id OR a.namespace_id IS NULL)) OR @@ -268,8 +263,8 @@ LIMIT 1 ` type getActionParams struct { - Name pgtype.Text `json:"name"` ID pgtype.UUID `json:"id"` + Name pgtype.Text `json:"name"` NamespaceID pgtype.UUID `json:"namespace_id"` NamespaceFqn pgtype.Text `json:"namespace_fqn"` } @@ -303,11 +298,6 @@ type getActionRow struct { // a.is_standard, // JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', a.metadata -> 'labels', 'created_at', a.created_at, 'updated_at', a.updated_at)) AS metadata, // CASE -// WHEN a.namespace_id IS NULL AND $1::text IS NOT NULL THEN JSON_BUILD_OBJECT( -// 'id', rn.id, -// 'name', rn.name, -// 'fqn', rn.fqn -// ) // WHEN a.namespace_id IS NULL THEN NULL // ELSE JSON_BUILD_OBJECT( // 'id', n.id, @@ -321,11 +311,11 @@ type getActionRow struct { // LEFT JOIN resolved_namespace rn ON TRUE // WHERE // ( -// ($2::uuid IS NOT NULL AND a.id = $2::uuid) +// ($1::uuid IS NOT NULL AND a.id = $1::uuid) // OR // ( -// $1::text IS NOT NULL -// AND a.name = $1::text +// $2::text IS NOT NULL +// AND a.name = $2::text // AND ( // (rn.id IS NOT NULL AND (a.namespace_id = rn.id OR a.namespace_id IS NULL)) // OR @@ -343,8 +333,8 @@ type getActionRow struct { // LIMIT 1 func (q *Queries) getAction(ctx context.Context, arg getActionParams) (getActionRow, error) { row := q.db.QueryRow(ctx, getAction, - arg.Name, arg.ID, + arg.Name, arg.NamespaceID, arg.NamespaceFqn, ) @@ -384,11 +374,6 @@ SELECT )) as metadata, a.is_standard, CASE - WHEN a.namespace_id IS NULL AND rn.id IS NOT NULL THEN JSON_BUILD_OBJECT( - 'id', rn.id, - 'name', rn.name, - 'fqn', rn.fqn - ) WHEN a.namespace_id IS NULL THEN NULL ELSE JSON_BUILD_OBJECT( 'id', n.id, @@ -457,11 +442,6 @@ type listActionsRow struct { // )) as metadata, // a.is_standard, // CASE -// WHEN a.namespace_id IS NULL AND rn.id IS NOT NULL THEN JSON_BUILD_OBJECT( -// 'id', rn.id, -// 'name', rn.name, -// 'fqn', rn.fqn -// ) // WHEN a.namespace_id IS NULL THEN NULL // ELSE JSON_BUILD_OBJECT( // 'id', n.id, diff --git a/service/policy/db/queries/actions.sql b/service/policy/db/queries/actions.sql index ed41517ab5..e3127569df 100644 --- a/service/policy/db/queries/actions.sql +++ b/service/policy/db/queries/actions.sql @@ -26,11 +26,6 @@ SELECT )) as metadata, a.is_standard, CASE - WHEN a.namespace_id IS NULL AND rn.id IS NOT NULL THEN JSON_BUILD_OBJECT( - 'id', rn.id, - 'name', rn.name, - 'fqn', rn.fqn - ) WHEN a.namespace_id IS NULL THEN NULL ELSE JSON_BUILD_OBJECT( 'id', n.id, @@ -75,11 +70,6 @@ SELECT a.is_standard, JSON_STRIP_NULLS(JSON_BUILD_OBJECT('labels', a.metadata -> 'labels', 'created_at', a.created_at, 'updated_at', a.updated_at)) AS metadata, CASE - WHEN a.namespace_id IS NULL AND sqlc.narg('name')::text IS NOT NULL THEN JSON_BUILD_OBJECT( - 'id', rn.id, - 'name', rn.name, - 'fqn', rn.fqn - ) WHEN a.namespace_id IS NULL THEN NULL ELSE JSON_BUILD_OBJECT( 'id', n.id, From 2df5af3c9954b3a2922b8c2a86505414d2e0617d Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Fri, 13 Mar 2026 14:52:23 -0400 Subject: [PATCH 04/10] dont include un-namespaced actions on list with namespace query --- service/integration/actions_test.go | 45 ++++++++++++++++++++------- service/policy/db/actions.sql.go | 2 -- service/policy/db/queries/actions.sql | 1 - 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/service/integration/actions_test.go b/service/integration/actions_test.go index 6f50c20d18..c8fcb2d53c 100644 --- a/service/integration/actions_test.go +++ b/service/integration/actions_test.go @@ -41,8 +41,20 @@ func (s *ActionsSuite) TearDownSuite() { } func (s *ActionsSuite) Test_ListActions_NoPagination_Succeeds() { - fixtureCustomAction1 := s.f.GetCustomActionKey("custom_action_1") - fixtureCustomAction2 := s.f.GetCustomActionKey("other_special_action") + name1 := fmt.Sprintf("scoped-list-nopage-1-%d", time.Now().UnixNano()) + name2 := fmt.Sprintf("scoped-list-nopage-2-%d", time.Now().UnixNano()) + + created1, err := s.db.PolicyClient.CreateAction(s.ctx, &actions.CreateActionRequest{ + Name: name1, + NamespaceId: s.defaultNamespaceID(), + }) + s.Require().NoError(err) + + created2, err := s.db.PolicyClient.CreateAction(s.ctx, &actions.CreateActionRequest{ + Name: name2, + NamespaceId: s.defaultNamespaceID(), + }) + s.Require().NoError(err) list, err := s.db.PolicyClient.ListActions(s.ctx, &actions.ListActionsRequest{NamespaceId: s.defaultNamespaceID()}) s.NotNil(list) @@ -56,10 +68,10 @@ func (s *ActionsSuite) Test_ListActions_NoPagination_Succeeds() { foundDelete := false for _, action := range list.GetActionsCustom() { - switch action.GetName() { - case fixtureCustomAction1.Name: + switch action.GetId() { + case created1.GetId(): foundCustomAction1 = true - case fixtureCustomAction2.Name: + case created2.GetId(): foundCustomAction2 = true } } @@ -222,27 +234,38 @@ func (s *ActionsSuite) Test_ListActions_WithoutNamespace_ReturnsAcrossNamespaces s.True(foundOther) } -func (s *ActionsSuite) Test_ListActions_LegacyCustomAction_NamespaceUnset_Succeeds() { +func (s *ActionsSuite) Test_ListActions_LegacyCustomAction_ScopedExcluded_UnscopedIncluded_Succeeds() { legacy := s.f.GetCustomActionKey("custom_action_1") - assertLegacyUnset := func(list *actions.ListActionsResponse) { + assertLegacyAbsent := func(list *actions.ListActionsResponse) { s.T().Helper() found := false for _, action := range list.GetActionsCustom() { if action.GetId() == legacy.ID { found = true - s.Nil(action.GetNamespace()) } } - s.True(found) + s.False(found) } listByID, err := s.db.PolicyClient.ListActions(s.ctx, &actions.ListActionsRequest{NamespaceId: s.defaultNamespaceID()}) s.Require().NoError(err) - assertLegacyUnset(listByID) + assertLegacyAbsent(listByID) listByFQN, err := s.db.PolicyClient.ListActions(s.ctx, &actions.ListActionsRequest{NamespaceFqn: s.defaultNamespaceFQN()}) s.Require().NoError(err) - assertLegacyUnset(listByFQN) + assertLegacyAbsent(listByFQN) + + listUnscoped, err := s.db.PolicyClient.ListActions(s.ctx, &actions.ListActionsRequest{}) + s.Require().NoError(err) + + foundUnscoped := false + for _, action := range listUnscoped.GetActionsCustom() { + if action.GetId() == legacy.ID { + foundUnscoped = true + s.Nil(action.GetNamespace()) + } + } + s.True(foundUnscoped) } func (s *ActionsSuite) Test_GetAction_Id_Succeeds() { diff --git a/service/policy/db/actions.sql.go b/service/policy/db/actions.sql.go index 54f95acef7..a375ad2c21 100644 --- a/service/policy/db/actions.sql.go +++ b/service/policy/db/actions.sql.go @@ -393,7 +393,6 @@ WHERE ) OR a.is_standard = TRUE OR a.namespace_id = rn.id - OR a.namespace_id IS NULL ORDER BY a.created_at DESC LIMIT $4 OFFSET $3 @@ -461,7 +460,6 @@ type listActionsRow struct { // ) // OR a.is_standard = TRUE // OR a.namespace_id = rn.id -// OR a.namespace_id IS NULL // ORDER BY a.created_at DESC // LIMIT $4 // OFFSET $3 diff --git a/service/policy/db/queries/actions.sql b/service/policy/db/queries/actions.sql index e3127569df..39737ded81 100644 --- a/service/policy/db/queries/actions.sql +++ b/service/policy/db/queries/actions.sql @@ -45,7 +45,6 @@ WHERE ) OR a.is_standard = TRUE OR a.namespace_id = rn.id - OR a.namespace_id IS NULL ORDER BY a.created_at DESC LIMIT @limit_ OFFSET @offset_; From 504eab630595bf6c3b65fdde40eee45ac1c65de5 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Mon, 16 Mar 2026 12:52:16 -0400 Subject: [PATCH 05/10] make namespace optional for getAction, if not provided assume namespace=null --- docs/grpc/index.html | 6 +- .../policy/actions/actions.openapi.yaml | 14 ++--- protocol/go/policy/actions/actions.pb.go | 24 +++----- service/integration/actions_test.go | 60 ++++++++++++------- service/policy/actions/actions.proto | 11 ++-- service/policy/actions/actions_test.go | 12 ---- service/policy/db/actions.sql.go | 4 +- service/policy/db/queries/actions.sql | 2 +- 8 files changed, 62 insertions(+), 71 deletions(-) diff --git a/docs/grpc/index.html b/docs/grpc/index.html index bb0bc3e41d..0979dc1a71 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -7565,14 +7565,16 @@

GetActionRequest

namespace_id string -

ID of the namespace. Required when identifier is set to name.

+

Optional namespace ID to scope name-based lookup. +If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions.

namespace_fqn string -

FQN of the namespace. Required when identifier is set to name.

+

Optional namespace FQN to scope name-based lookup. +If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions.

diff --git a/docs/openapi/policy/actions/actions.openapi.yaml b/docs/openapi/policy/actions/actions.openapi.yaml index 70fdd4d068..a63e989719 100644 --- a/docs/openapi/policy/actions/actions.openapi.yaml +++ b/docs/openapi/policy/actions/actions.openapi.yaml @@ -1136,21 +1136,19 @@ components: type: string title: namespace_id format: uuid - description: ID of the namespace. Required when identifier is set to name. + description: |- + Optional namespace ID to scope name-based lookup. + If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions. namespaceFqn: type: string title: namespace_fqn minLength: 1 format: uri - description: FQN of the namespace. Required when identifier is set to name. + description: |- + Optional namespace FQN to scope name-based lookup. + If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions. title: GetActionRequest additionalProperties: false - description: |+ - Either namespace_id or namespace_fqn must be provided when getting an action by name: - ``` - !has(this.name) || this.namespace_id != '' || this.namespace_fqn != '' - ``` - policy.actions.GetActionResponse: type: object properties: diff --git a/protocol/go/policy/actions/actions.pb.go b/protocol/go/policy/actions/actions.pb.go index 9cada6c683..44a1e92e6f 100644 --- a/protocol/go/policy/actions/actions.pb.go +++ b/protocol/go/policy/actions/actions.pb.go @@ -35,9 +35,11 @@ type GetActionRequest struct { // *GetActionRequest_Id // *GetActionRequest_Name Identifier isGetActionRequest_Identifier `protobuf_oneof:"identifier"` - // ID of the namespace. Required when identifier is set to name. + // Optional namespace ID to scope name-based lookup. + // If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions. NamespaceId string `protobuf:"bytes,3,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - // FQN of the namespace. Required when identifier is set to name. + // Optional namespace FQN to scope name-based lookup. + // If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions. NamespaceFqn string `protobuf:"bytes,4,opt,name=namespace_fqn,json=namespaceFqn,proto3" json:"namespace_fqn,omitempty"` } @@ -664,8 +666,8 @@ var file_policy_actions_actions_proto_rawDesc = []byte{ 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x14, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x16, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x73, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb5, - 0x05, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf5, + 0x03, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0xa5, 0x02, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x8e, @@ -692,19 +694,7 @@ var file_policy_actions_actions_proto_rawDesc = []byte{ 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0d, 0xba, 0x48, 0x0a, 0xd8, 0x01, 0x01, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x52, 0x0c, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, 0x71, 0x6e, 0x3a, 0xe3, 0x01, 0xba, 0x48, - 0xdf, 0x01, 0x1a, 0xbb, 0x01, 0x0a, 0x1b, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x54, 0x45, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x6f, 0x72, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, - 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x67, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x61, 0x6e, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x20, 0x62, 0x79, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x1a, 0x46, 0x21, 0x68, 0x61, 0x73, 0x28, 0x74, - 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, - 0x73, 0x2e, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x20, 0x21, - 0x3d, 0x20, 0x27, 0x27, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x61, 0x6d, - 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x46, 0x71, 0x6e, 0x3a, 0x24, 0xba, 0x48, 0x21, 0x22, 0x1f, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x10, 0x00, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, diff --git a/service/integration/actions_test.go b/service/integration/actions_test.go index c8fcb2d53c..c60a839aac 100644 --- a/service/integration/actions_test.go +++ b/service/integration/actions_test.go @@ -297,30 +297,26 @@ func (s *ActionsSuite) Test_GetAction_Id_Succeeds() { } func (s *ActionsSuite) Test_GetAction_Name_Succeeds() { - customAction := s.f.GetCustomActionKey("other_special_action") - actionCreate := s.f.GetStandardAction(policydb.ActionCreate.String()) - action, err := s.db.PolicyClient.GetAction(s.ctx, &actions.GetActionRequest{ - Identifier: &actions.GetActionRequest_Name{ - Name: customAction.Name, - }, + name := fmt.Sprintf("get-by-name-action-%d", time.Now().UnixNano()) + customAction, err := s.db.PolicyClient.CreateAction(s.ctx, &actions.CreateActionRequest{ + Name: name, NamespaceId: s.defaultNamespaceID(), }) - s.NotNil(action) s.Require().NoError(err) - s.Equal(customAction.ID, action.GetId()) - s.Equal(customAction.Name, action.GetName()) - s.NotNil(action.GetMetadata()) + s.NotNil(customAction) - action, err = s.db.PolicyClient.GetAction(s.ctx, &actions.GetActionRequest{ + action, err := s.db.PolicyClient.GetAction(s.ctx, &actions.GetActionRequest{ Identifier: &actions.GetActionRequest_Name{ - Name: actionCreate.GetName(), + Name: customAction.GetName(), }, NamespaceId: s.defaultNamespaceID(), }) s.NotNil(action) s.Require().NoError(err) - s.Equal(actionCreate.GetName(), action.GetName()) - s.Equal(actionCreate.GetId(), action.GetId()) + s.Equal(customAction.GetId(), action.GetId()) + s.Equal(customAction.GetName(), action.GetName()) + s.Require().NotNil(action.GetNamespace()) + s.Equal(s.defaultNamespaceID(), action.GetNamespace().GetId()) s.NotNil(action.GetMetadata()) } @@ -356,27 +352,47 @@ func (s *ActionsSuite) Test_GetAction_Name_ResolvesByNamespace_Succeeds() { s.Equal(s.otherNamespaceID(), gotOther.GetNamespace().GetId()) } -func (s *ActionsSuite) Test_GetAction_Name_LegacyCustomAction_Succeeds() { +func (s *ActionsSuite) Test_GetAction_Name_LegacyCustomAction_UnscopedSucceeds_ScopedFails() { legacy := s.f.GetCustomActionKey("other_special_action") - assertLegacyGet := func(action *policy.Action) { + + byUnscoped, err := s.db.PolicyClient.GetAction(s.ctx, &actions.GetActionRequest{ + Identifier: &actions.GetActionRequest_Name{Name: legacy.Name}, + }) + s.Require().NoError(err) + s.NotNil(byUnscoped) + s.Equal(legacy.ID, byUnscoped.GetId()) + s.Nil(byUnscoped.GetNamespace()) + + assertLegacyGet := func(action *policy.Action, err error) { s.T().Helper() - s.Equal(legacy.ID, action.GetId()) - s.Nil(action.GetNamespace()) + s.Nil(action) + s.Require().Error(err) + s.Require().ErrorIs(err, db.ErrNotFound) } byID, err := s.db.PolicyClient.GetAction(s.ctx, &actions.GetActionRequest{ Identifier: &actions.GetActionRequest_Name{Name: legacy.Name}, NamespaceId: s.defaultNamespaceID(), }) - s.Require().NoError(err) - assertLegacyGet(byID) + assertLegacyGet(byID, err) byFQN, err := s.db.PolicyClient.GetAction(s.ctx, &actions.GetActionRequest{ Identifier: &actions.GetActionRequest_Name{Name: legacy.Name}, NamespaceFqn: s.defaultNamespaceFQN(), }) - s.Require().NoError(err) - assertLegacyGet(byFQN) + assertLegacyGet(byFQN, err) + + byOtherID, err := s.db.PolicyClient.GetAction(s.ctx, &actions.GetActionRequest{ + Identifier: &actions.GetActionRequest_Name{Name: legacy.Name}, + NamespaceId: s.otherNamespaceID(), + }) + assertLegacyGet(byOtherID, err) + + byOtherFQN, err := s.db.PolicyClient.GetAction(s.ctx, &actions.GetActionRequest{ + Identifier: &actions.GetActionRequest_Name{Name: legacy.Name}, + NamespaceFqn: s.otherNamespaceFQN(), + }) + assertLegacyGet(byOtherFQN, err) } func (s *ActionsSuite) Test_CreateListGetAction_WithNamespaceFQN_Succeeds() { diff --git a/service/policy/actions/actions.proto b/service/policy/actions/actions.proto index 8a8d882f26..05afcb6f54 100644 --- a/service/policy/actions/actions.proto +++ b/service/policy/actions/actions.proto @@ -13,11 +13,6 @@ import "policy/selectors.proto"; message GetActionRequest { option (buf.validate.message).oneof = { fields: ["namespace_id", "namespace_fqn"], required: false }; - option (buf.validate.message).cel = { - id: "namespace_required_for_name" - message: "Either namespace_id or namespace_fqn must be provided when getting an action by name" - expression: "!has(this.name) || this.namespace_id != '' || this.namespace_fqn != ''" - }; // Required oneof identifier { @@ -33,12 +28,14 @@ message GetActionRequest { ]; } - // ID of the namespace. Required when identifier is set to name. + // Optional namespace ID to scope name-based lookup. + // If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions. string namespace_id = 3 [ (buf.validate.field).ignore = IGNORE_IF_ZERO_VALUE, (buf.validate.field).string.uuid = true ]; - // FQN of the namespace. Required when identifier is set to name. + // Optional namespace FQN to scope name-based lookup. + // If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions. string namespace_fqn = 4 [ (buf.validate.field).ignore = IGNORE_IF_ZERO_VALUE, (buf.validate.field).string = { diff --git a/service/policy/actions/actions_test.go b/service/policy/actions/actions_test.go index 87429d93ff..1f2e626a69 100644 --- a/service/policy/actions/actions_test.go +++ b/service/policy/actions/actions_test.go @@ -20,7 +20,6 @@ const ( errMessageURI = "string.uri" errMessageRequired = "required" errMessageOneof = "message.oneof" - errMessageNamespaceByName = "namespace_required_for_name" ) var ( @@ -157,7 +156,6 @@ func (s *ActionSuite) Test_GetAction_Succeeds() { Identifier: &actions.GetActionRequest_Name{ Name: name, }, - NamespaceFqn: validNamespaceFQN, } err := s.v.Validate(req) s.Require().NoError(err) @@ -181,16 +179,6 @@ func (s *ActionSuite) Test_GetAction_Fails() { s.Require().Error(err) s.Require().Contains(err.Error(), errMessageRequired) - // missing namespace - req = &actions.GetActionRequest{ - Identifier: &actions.GetActionRequest_Name{ - Name: "valid_name", - }, - } - err = s.v.Validate(req) - s.Require().Error(err) - s.Require().Contains(err.Error(), errMessageNamespaceByName) - for _, name := range actionNamesInvalidFormat { s.Run(name, func() { req = &actions.GetActionRequest{ diff --git a/service/policy/db/actions.sql.go b/service/policy/db/actions.sql.go index a375ad2c21..a51f83cb3a 100644 --- a/service/policy/db/actions.sql.go +++ b/service/policy/db/actions.sql.go @@ -246,7 +246,7 @@ WHERE $2::text IS NOT NULL AND a.name = $2::text AND ( - (rn.id IS NOT NULL AND (a.namespace_id = rn.id OR a.namespace_id IS NULL)) + (rn.id IS NOT NULL AND a.namespace_id = rn.id) OR (rn.id IS NULL AND a.namespace_id IS NULL) ) @@ -317,7 +317,7 @@ type getActionRow struct { // $2::text IS NOT NULL // AND a.name = $2::text // AND ( -// (rn.id IS NOT NULL AND (a.namespace_id = rn.id OR a.namespace_id IS NULL)) +// (rn.id IS NOT NULL AND a.namespace_id = rn.id) // OR // (rn.id IS NULL AND a.namespace_id IS NULL) // ) diff --git a/service/policy/db/queries/actions.sql b/service/policy/db/queries/actions.sql index 39737ded81..0deded4ce4 100644 --- a/service/policy/db/queries/actions.sql +++ b/service/policy/db/queries/actions.sql @@ -88,7 +88,7 @@ WHERE sqlc.narg('name')::text IS NOT NULL AND a.name = sqlc.narg('name')::text AND ( - (rn.id IS NOT NULL AND (a.namespace_id = rn.id OR a.namespace_id IS NULL)) + (rn.id IS NOT NULL AND a.namespace_id = rn.id) OR (rn.id IS NULL AND a.namespace_id IS NULL) ) From f30ab9205db4aa46ea1340597ec8e592d494c939 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Mon, 16 Mar 2026 13:05:47 -0400 Subject: [PATCH 06/10] feature flag, everything optional --- service/integration/actions_test.go | 9 +++++++++ service/policy/actions/actions.go | 5 +++++ service/policy/config/config.go | 2 ++ service/policy/db/actions.go | 3 ++- service/policy/db/actions.sql.go | 4 ++++ service/policy/db/queries/actions.sql | 2 ++ 6 files changed, 24 insertions(+), 1 deletion(-) diff --git a/service/integration/actions_test.go b/service/integration/actions_test.go index c60a839aac..35cc119be0 100644 --- a/service/integration/actions_test.go +++ b/service/integration/actions_test.go @@ -489,6 +489,15 @@ func (s *ActionsSuite) Test_CreateAction_Conflict_Fails() { s.Require().ErrorIs(err, db.ErrUniqueConstraintViolation) } +func (s *ActionsSuite) Test_CreateAction_MissingNamespace_SucceedsInLegacyMode() { + action, err := s.db.PolicyClient.CreateAction(s.ctx, &actions.CreateActionRequest{ + Name: fmt.Sprintf("missing-namespace-%d", time.Now().UnixNano()), + }) + s.Require().NoError(err) + s.NotNil(action) + s.Nil(action.GetNamespace()) +} + func (s *ActionsSuite) Test_CreateAction_NormalizesToLowerCase() { newName := "New_Custom_Action_CreateAction_UPPER" action, err := s.db.PolicyClient.CreateAction(s.ctx, &actions.CreateActionRequest{ diff --git a/service/policy/actions/actions.go b/service/policy/actions/actions.go index fe263f1030..6c4f5ca5e9 100644 --- a/service/policy/actions/actions.go +++ b/service/policy/actions/actions.go @@ -2,6 +2,7 @@ package actions import ( "context" + "errors" "fmt" "log/slog" @@ -112,6 +113,10 @@ func (a *ActionService) ListActions(ctx context.Context, req *connect.Request[ac func (a *ActionService) CreateAction(ctx context.Context, req *connect.Request[actions.CreateActionRequest]) (*connect.Response[actions.CreateActionResponse], error) { a.logger.DebugContext(ctx, "creating action", slog.String("name", req.Msg.GetName())) + if a.config.NamespacedPolicy && req.Msg.GetNamespaceId() == "" && req.Msg.GetNamespaceFqn() == "" { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("either namespace_id or namespace_fqn must be provided")) + } + auditParams := audit.PolicyEventParams{ ActionType: audit.ActionTypeCreate, ObjectType: audit.ObjectTypeAction, diff --git a/service/policy/config/config.go b/service/policy/config/config.go index 0f7a74dace..d237001aa7 100644 --- a/service/policy/config/config.go +++ b/service/policy/config/config.go @@ -15,6 +15,8 @@ type Config struct { ListRequestLimitDefault int `mapstructure:"list_request_limit_default" default:"1000"` // Maximum pagination list limit allowed by policy services ListRequestLimitMax int `mapstructure:"list_request_limit_max" default:"2500"` + // Enable support for namespaced policies. If false, namespace fields are ignored and treated as null. + NamespacedPolicy bool `mapstructure:"namespaced_policy" default:"false"` } func (c Config) Validate() error { diff --git a/service/policy/db/actions.go b/service/policy/db/actions.go index adef373dfa..fc212dd126 100644 --- a/service/policy/db/actions.go +++ b/service/policy/db/actions.go @@ -150,6 +150,7 @@ func (c PolicyDBClient) CreateAction(ctx context.Context, req *actions.CreateAct } namespaceID := req.GetNamespaceId() + namespaceFQN := req.GetNamespaceFqn() useID := len(namespaceID) > 0 parsedID := pgtypeUUID(namespaceID) if useID && !parsedID.Valid { @@ -164,7 +165,7 @@ func (c PolicyDBClient) CreateAction(ctx context.Context, req *actions.CreateAct Name: name, Metadata: metadataJSON, NamespaceID: parsedID, - NamespaceFqn: pgtypeText(req.GetNamespaceFqn()), + NamespaceFqn: pgtypeText(namespaceFQN), } createdID, err := c.queries.createCustomAction(ctx, createParams) diff --git a/service/policy/db/actions.sql.go b/service/policy/db/actions.sql.go index a51f83cb3a..e9fc2875c2 100644 --- a/service/policy/db/actions.sql.go +++ b/service/policy/db/actions.sql.go @@ -26,6 +26,8 @@ SELECT FROM ns LEFT JOIN attribute_fqns fqns ON fqns.fqn = ns.fqn AND ns.id IS NULL WHERE + (ns.id IS NULL AND ns.fqn IS NULL) + OR (ns.id IS NOT NULL) OR (ns.fqn IS NOT NULL AND fqns.namespace_id IS NOT NULL) @@ -55,6 +57,8 @@ type createCustomActionParams struct { // FROM ns // LEFT JOIN attribute_fqns fqns ON fqns.fqn = ns.fqn AND ns.id IS NULL // WHERE +// (ns.id IS NULL AND ns.fqn IS NULL) +// OR // (ns.id IS NOT NULL) // OR // (ns.fqn IS NOT NULL AND fqns.namespace_id IS NOT NULL) diff --git a/service/policy/db/queries/actions.sql b/service/policy/db/queries/actions.sql index 0deded4ce4..718f5342b9 100644 --- a/service/policy/db/queries/actions.sql +++ b/service/policy/db/queries/actions.sql @@ -159,6 +159,8 @@ SELECT FROM ns LEFT JOIN attribute_fqns fqns ON fqns.fqn = ns.fqn AND ns.id IS NULL WHERE + (ns.id IS NULL AND ns.fqn IS NULL) + OR (ns.id IS NOT NULL) OR (ns.fqn IS NOT NULL AND fqns.namespace_id IS NOT NULL) From da2b6b503f651aae23a20d2f300915e6e2f27117 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Mon, 16 Mar 2026 13:08:54 -0400 Subject: [PATCH 07/10] update test --- service/integration/actions_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/service/integration/actions_test.go b/service/integration/actions_test.go index 35cc119be0..31ef368af1 100644 --- a/service/integration/actions_test.go +++ b/service/integration/actions_test.go @@ -236,6 +236,14 @@ func (s *ActionsSuite) Test_ListActions_WithoutNamespace_ReturnsAcrossNamespaces func (s *ActionsSuite) Test_ListActions_LegacyCustomAction_ScopedExcluded_UnscopedIncluded_Succeeds() { legacy := s.f.GetCustomActionKey("custom_action_1") + scopedName := fmt.Sprintf("scoped-list-action-%d", time.Now().UnixNano()) + scoped, err := s.db.PolicyClient.CreateAction(s.ctx, &actions.CreateActionRequest{ + Name: scopedName, + NamespaceId: s.defaultNamespaceID(), + }) + s.Require().NoError(err) + s.NotNil(scoped) + assertLegacyAbsent := func(list *actions.ListActionsResponse) { s.T().Helper() found := false @@ -259,13 +267,20 @@ func (s *ActionsSuite) Test_ListActions_LegacyCustomAction_ScopedExcluded_Unscop s.Require().NoError(err) foundUnscoped := false + foundScoped := false for _, action := range listUnscoped.GetActionsCustom() { if action.GetId() == legacy.ID { foundUnscoped = true s.Nil(action.GetNamespace()) } + if action.GetId() == scoped.GetId() { + foundScoped = true + s.Require().NotNil(action.GetNamespace()) + s.Equal(s.defaultNamespaceID(), action.GetNamespace().GetId()) + } } s.True(foundUnscoped) + s.True(foundScoped) } func (s *ActionsSuite) Test_GetAction_Id_Succeeds() { From 89ca5b2bb778d39d0b3bca9481bbe7e55a3f3762 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Mon, 16 Mar 2026 13:23:54 -0400 Subject: [PATCH 08/10] fix registered resources --- .../integration/registered_resources_test.go | 39 ------------------- service/policy/db/registered_resources.go | 3 +- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/service/integration/registered_resources_test.go b/service/integration/registered_resources_test.go index 1067bb6aa8..afba4e8d71 100644 --- a/service/integration/registered_resources_test.go +++ b/service/integration/registered_resources_test.go @@ -1939,45 +1939,6 @@ func (s *RegisteredResourcesSuite) Test_SameNamespaceEnforcement_SameNamespace_S s.Require().Len(resVal.GetActionAttributeValues(), 1) } -func (s *RegisteredResourcesSuite) Test_CreateRegisteredResourceValue_WithNamespacedCustomActionName_Succeeds() { - nsID := s.getNamespaceID("example.com") - - res, err := s.db.PolicyClient.CreateRegisteredResource(s.ctx, ®isteredresources.CreateRegisteredResourceRequest{ - NamespaceId: nsID, - Name: fmt.Sprintf("test_rr_custom_action_name_%d", time.Now().UnixNano()), - }) - s.Require().NoError(err) - s.NotNil(res) - - customActionName := fmt.Sprintf("rr_custom_action_%d", time.Now().UnixNano()) - customAction, err := s.db.PolicyClient.CreateAction(s.ctx, &pbActions.CreateActionRequest{ - Name: customActionName, - NamespaceId: nsID, - }) - s.Require().NoError(err) - s.NotNil(customAction) - - resVal, err := s.db.PolicyClient.CreateRegisteredResourceValue(s.ctx, ®isteredresources.CreateRegisteredResourceValueRequest{ - ResourceId: res.GetId(), - Value: fmt.Sprintf("test_rr_custom_action_name_value_%d", time.Now().UnixNano()), - ActionAttributeValues: []*registeredresources.ActionAttributeValue{ - { - ActionIdentifier: ®isteredresources.ActionAttributeValue_ActionName{ - ActionName: customActionName, - }, - AttributeValueIdentifier: ®isteredresources.ActionAttributeValue_AttributeValueFqn{ - AttributeValueFqn: "https://example.com/attr/attr1/value/value1", - }, - }, - }, - }) - s.Require().NoError(err) - s.NotNil(resVal) - s.Require().Len(resVal.GetActionAttributeValues(), 1) - s.Equal(customAction.GetId(), resVal.GetActionAttributeValues()[0].GetAction().GetId()) - s.Equal(customActionName, resVal.GetActionAttributeValues()[0].GetAction().GetName()) -} - func (s *RegisteredResourcesSuite) getNamespaceID(key string) string { ns := s.f.GetNamespaceKey(key) return ns.ID diff --git a/service/policy/db/registered_resources.go b/service/policy/db/registered_resources.go index a14a7965d9..61d3109d54 100644 --- a/service/policy/db/registered_resources.go +++ b/service/policy/db/registered_resources.go @@ -585,8 +585,7 @@ func (c PolicyDBClient) createRegisteredResourceActionAttributeValues(ctx contex actionID = ident.ActionId case *registeredresources.ActionAttributeValue_ActionName: a, err := c.queries.getAction(ctx, getActionParams{ - Name: pgtypeText(strings.ToLower(ident.ActionName)), - NamespaceID: pgtypeUUID(resourceNamespaceID), + Name: pgtypeText(strings.ToLower(ident.ActionName)), }) if err != nil { return db.WrapIfKnownInvalidQueryErr(err) From 7386b80c3cc6a10c8355b19f5633476378663ee2 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Mon, 16 Mar 2026 13:36:15 -0400 Subject: [PATCH 09/10] fix create proto --- protocol/go/policy/actions/actions.pb.go | 2 +- service/policy/actions/actions.proto | 2 +- service/policy/actions/actions_test.go | 20 +++++++++----------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/protocol/go/policy/actions/actions.pb.go b/protocol/go/policy/actions/actions.pb.go index 44a1e92e6f..b1310cec3a 100644 --- a/protocol/go/policy/actions/actions.pb.go +++ b/protocol/go/policy/actions/actions.pb.go @@ -762,7 +762,7 @@ var file_policy_actions_actions_proto_rawDesc = []byte{ 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x3a, 0x24, 0xba, 0x48, 0x21, 0x22, 0x1f, 0x0a, 0x0c, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x0a, - 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x10, 0x01, + 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x66, 0x71, 0x6e, 0x10, 0x00, 0x22, 0x3e, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, diff --git a/service/policy/actions/actions.proto b/service/policy/actions/actions.proto index 05afcb6f54..6bdf690740 100644 --- a/service/policy/actions/actions.proto +++ b/service/policy/actions/actions.proto @@ -81,7 +81,7 @@ message ListActionsResponse { // Creation of Standard actions is not supported. message CreateActionRequest { // Required - option (buf.validate.message).oneof = { fields: ["namespace_id", "namespace_fqn"], required: true }; + option (buf.validate.message).oneof = { fields: ["namespace_id", "namespace_fqn"], required: false }; // Required string name = 1 [ diff --git a/service/policy/actions/actions_test.go b/service/policy/actions/actions_test.go index 1f2e626a69..fd5daf39ef 100644 --- a/service/policy/actions/actions_test.go +++ b/service/policy/actions/actions_test.go @@ -19,7 +19,6 @@ const ( errMessageActionNameFormat = "action_name_format" errMessageURI = "string.uri" errMessageRequired = "required" - errMessageOneof = "message.oneof" ) var ( @@ -90,14 +89,6 @@ func (s *ActionSuite) Test_CreateActionRequest_Fails() { s.Require().Error(err) s.Require().Contains(err.Error(), errMessageMaxLength) - // namespace required - req = &actions.CreateActionRequest{ - Name: "valid_name", - } - err = s.v.Validate(req) - s.Require().Error(err) - s.Require().Contains(err.Error(), errMessageOneof) - // invalid namespace id req = &actions.CreateActionRequest{ Name: "valid_name", @@ -129,15 +120,22 @@ func (s *ActionSuite) Test_CreateActionRequest_Succeeds() { }) } - // with metadata + // with no namespace req := &actions.CreateActionRequest{ + Name: "valid_name", + } + err := s.v.Validate(req) + s.Require().NoError(err) + + // with metadata + req = &actions.CreateActionRequest{ Name: "valid_name", NamespaceId: validUUID, Metadata: &common.MetadataMutable{ Labels: map[string]string{"key": "value"}, }, } - err := s.v.Validate(req) + err = s.v.Validate(req) s.Require().NoError(err) } From 498f80ff78708ac437383460851361eb93d38e58 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Mon, 16 Mar 2026 13:43:43 -0400 Subject: [PATCH 10/10] update proto comments --- docs/grpc/index.html | 6 ++++-- docs/openapi/policy/actions/actions.openapi.yaml | 8 ++++++-- protocol/go/policy/actions/actions.pb.go | 6 ++++-- service/policy/actions/actions.proto | 8 +++++--- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/grpc/index.html b/docs/grpc/index.html index 0979dc1a71..230b876c28 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -7441,14 +7441,16 @@

CreateActionRequest

namespace_id string -

ID of the namespace. Required if namespace_fqn is not provided.

+

Optional namespace ID for the custom action. +If omitted, create targets legacy (namespace_id = NULL) behavior unless enforced by server config.

namespace_fqn string -

FQN of the namespace. Required if namespace_id is not provided.

+

Optional namespace FQN for the custom action. +If omitted, create targets legacy (namespace_id = NULL) behavior unless enforced by server config.

diff --git a/docs/openapi/policy/actions/actions.openapi.yaml b/docs/openapi/policy/actions/actions.openapi.yaml index a63e989719..3ec25cd9f6 100644 --- a/docs/openapi/policy/actions/actions.openapi.yaml +++ b/docs/openapi/policy/actions/actions.openapi.yaml @@ -1061,13 +1061,17 @@ components: type: string title: namespace_id format: uuid - description: ID of the namespace. Required if namespace_fqn is not provided. + description: |- + Optional namespace ID for the custom action. + If omitted, create targets legacy (namespace_id = NULL) behavior unless enforced by server config. namespaceFqn: type: string title: namespace_fqn minLength: 1 format: uri - description: FQN of the namespace. Required if namespace_id is not provided. + description: |- + Optional namespace FQN for the custom action. + If omitted, create targets legacy (namespace_id = NULL) behavior unless enforced by server config. metadata: title: metadata description: Optional diff --git a/protocol/go/policy/actions/actions.pb.go b/protocol/go/policy/actions/actions.pb.go index b1310cec3a..66e8322dce 100644 --- a/protocol/go/policy/actions/actions.pb.go +++ b/protocol/go/policy/actions/actions.pb.go @@ -320,9 +320,11 @@ type CreateActionRequest struct { // Required Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // ID of the namespace. Required if namespace_fqn is not provided. + // Optional namespace ID for the custom action. + // If omitted, create targets legacy (namespace_id = NULL) behavior unless enforced by server config. NamespaceId string `protobuf:"bytes,2,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` - // FQN of the namespace. Required if namespace_id is not provided. + // Optional namespace FQN for the custom action. + // If omitted, create targets legacy (namespace_id = NULL) behavior unless enforced by server config. NamespaceFqn string `protobuf:"bytes,3,opt,name=namespace_fqn,json=namespaceFqn,proto3" json:"namespace_fqn,omitempty"` // Optional Metadata *common.MetadataMutable `protobuf:"bytes,100,opt,name=metadata,proto3" json:"metadata,omitempty"` diff --git a/service/policy/actions/actions.proto b/service/policy/actions/actions.proto index 6bdf690740..7065e2d503 100644 --- a/service/policy/actions/actions.proto +++ b/service/policy/actions/actions.proto @@ -80,7 +80,7 @@ message ListActionsResponse { // Create a new Custom action name with optional metadata. // Creation of Standard actions is not supported. message CreateActionRequest { - // Required + // Optional option (buf.validate.message).oneof = { fields: ["namespace_id", "namespace_fqn"], required: false }; // Required @@ -94,9 +94,11 @@ message CreateActionRequest { } ]; - // ID of the namespace. Required if namespace_fqn is not provided. + // Optional namespace ID for the custom action. + // If omitted, create targets legacy (namespace_id = NULL) behavior unless enforced by server config. string namespace_id = 2 [(buf.validate.field).string.uuid = true]; - // FQN of the namespace. Required if namespace_id is not provided. + // Optional namespace FQN for the custom action. + // If omitted, create targets legacy (namespace_id = NULL) behavior unless enforced by server config. string namespace_fqn = 3 [ (buf.validate.field).string = { min_len : 1