diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index 1e79df208..0d41edcab 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -70,9 +70,9 @@ jobs: - name: Ensure "make gomodtidy" has been run run: | make gomodtidy - git diff --exit-code + git diff --minimal --exit-code - name: Ensure "make generate" has been run run: | make rm-mocked make generate - git diff --exit-code + git diff --stat --exit-code diff --git a/Makefile b/Makefile index d14fbe647..5ae713cb1 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,10 @@ +.PHONY: gomods +gomods: ## Install gomods + go install github.com/jmank88/gomods@v0.1.3 + .PHONY: gomodtidy -gomodtidy: - go mod tidy +gomodtidy: gomods + gomods tidy .PHONY: docs docs: @@ -22,11 +26,9 @@ rm-mocked: grep -rl "^// Code generated by mockery" | grep .go$ | xargs -r rm .PHONY: generate -generate: mockery install-protoc - # add our installed protoc to the head of the PATH - # maybe there is a cleaner way to do this - PATH=$$HOME/.local/bin:$$PATH go generate -x ./...; \ - mockery +generate: mockery install-protoc gomods + gomods -w go generate -x ./... + mockery .PHONY: lint-workspace lint GOLANGCI_LINT_VERSION := 1.55.2 diff --git a/observability-lib/go.sum b/observability-lib/go.sum index 4d700e03b..e9570739c 100644 --- a/observability-lib/go.sum +++ b/observability-lib/go.sum @@ -46,8 +46,6 @@ github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/pkg/capabilities/capabilities.go b/pkg/capabilities/capabilities.go index da29e0331..3c4e5806f 100644 --- a/pkg/capabilities/capabilities.go +++ b/pkg/capabilities/capabilities.go @@ -398,6 +398,10 @@ type RemoteTriggerConfig struct { MessageExpiry time.Duration } +type RemoteTargetConfig struct { + RequestHashExcludedAttributes []string +} + // NOTE: consider splitting this config into values stored in Registry (KS-118) // and values defined locally by Capability owners. func (c *RemoteTriggerConfig) ApplyDefaults() { @@ -414,5 +418,6 @@ func (c *RemoteTriggerConfig) ApplyDefaults() { type CapabilityConfiguration struct { DefaultConfig *values.Map - RemoteTriggerConfig RemoteTriggerConfig + RemoteTriggerConfig *RemoteTriggerConfig + RemoteTargetConfig *RemoteTargetConfig } diff --git a/pkg/capabilities/pb/registry.pb.go b/pkg/capabilities/pb/registry.pb.go index ef06eec49..2254d3822 100644 --- a/pkg/capabilities/pb/registry.pb.go +++ b/pkg/capabilities/pb/registry.pb.go @@ -93,6 +93,56 @@ func (x *RemoteTriggerConfig) GetMessageExpiry() *durationpb.Duration { return nil } +type RemoteTargetConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // A collection of dot seperated paths to attributes that should be excluded from the request sent to the remote target + // when calculating the hash of the request. This is useful for excluding attributes that are not deterministic to ensure + // that the hash of logically identical requests is consistent. + RequestHashExcludedAttributes []string `protobuf:"bytes,1,rep,name=requestHashExcludedAttributes,proto3" json:"requestHashExcludedAttributes,omitempty"` +} + +func (x *RemoteTargetConfig) Reset() { + *x = RemoteTargetConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_capabilities_pb_registry_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RemoteTargetConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoteTargetConfig) ProtoMessage() {} + +func (x *RemoteTargetConfig) ProtoReflect() protoreflect.Message { + mi := &file_capabilities_pb_registry_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoteTargetConfig.ProtoReflect.Descriptor instead. +func (*RemoteTargetConfig) Descriptor() ([]byte, []int) { + return file_capabilities_pb_registry_proto_rawDescGZIP(), []int{1} +} + +func (x *RemoteTargetConfig) GetRequestHashExcludedAttributes() []string { + if x != nil { + return x.RequestHashExcludedAttributes + } + return nil +} + type CapabilityConfig struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -102,13 +152,14 @@ type CapabilityConfig struct { // Types that are assignable to RemoteConfig: // // *CapabilityConfig_RemoteTriggerConfig + // *CapabilityConfig_RemoteTargetConfig RemoteConfig isCapabilityConfig_RemoteConfig `protobuf_oneof:"remote_config"` } func (x *CapabilityConfig) Reset() { *x = CapabilityConfig{} if protoimpl.UnsafeEnabled { - mi := &file_capabilities_pb_registry_proto_msgTypes[1] + mi := &file_capabilities_pb_registry_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -121,7 +172,7 @@ func (x *CapabilityConfig) String() string { func (*CapabilityConfig) ProtoMessage() {} func (x *CapabilityConfig) ProtoReflect() protoreflect.Message { - mi := &file_capabilities_pb_registry_proto_msgTypes[1] + mi := &file_capabilities_pb_registry_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -134,7 +185,7 @@ func (x *CapabilityConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use CapabilityConfig.ProtoReflect.Descriptor instead. func (*CapabilityConfig) Descriptor() ([]byte, []int) { - return file_capabilities_pb_registry_proto_rawDescGZIP(), []int{1} + return file_capabilities_pb_registry_proto_rawDescGZIP(), []int{2} } func (x *CapabilityConfig) GetDefaultConfig() *pb.Map { @@ -158,6 +209,13 @@ func (x *CapabilityConfig) GetRemoteTriggerConfig() *RemoteTriggerConfig { return nil } +func (x *CapabilityConfig) GetRemoteTargetConfig() *RemoteTargetConfig { + if x, ok := x.GetRemoteConfig().(*CapabilityConfig_RemoteTargetConfig); ok { + return x.RemoteTargetConfig + } + return nil +} + type isCapabilityConfig_RemoteConfig interface { isCapabilityConfig_RemoteConfig() } @@ -166,8 +224,14 @@ type CapabilityConfig_RemoteTriggerConfig struct { RemoteTriggerConfig *RemoteTriggerConfig `protobuf:"bytes,2,opt,name=remote_trigger_config,json=remoteTriggerConfig,proto3,oneof"` } +type CapabilityConfig_RemoteTargetConfig struct { + RemoteTargetConfig *RemoteTargetConfig `protobuf:"bytes,3,opt,name=remote_target_config,json=remoteTargetConfig,proto3,oneof"` +} + func (*CapabilityConfig_RemoteTriggerConfig) isCapabilityConfig_RemoteConfig() {} +func (*CapabilityConfig_RemoteTargetConfig) isCapabilityConfig_RemoteConfig() {} + var File_capabilities_pb_registry_proto protoreflect.FileDescriptor var file_capabilities_pb_registry_proto_rawDesc = []byte{ @@ -195,22 +259,33 @@ var file_capabilities_pb_registry_proto_rawDesc = []byte{ 0x61, 0x67, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x22, 0xa8, 0x01, 0x0a, 0x10, 0x43, 0x61, - 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x32, - 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, - 0x4d, 0x61, 0x70, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x4f, 0x0a, 0x15, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x74, 0x72, 0x69, - 0x67, 0x67, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x54, - 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x13, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x42, 0x0f, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, 0x72, 0x74, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, - 0x6b, 0x69, 0x74, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x67, 0x65, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x22, 0x5a, 0x0a, 0x12, 0x52, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x44, 0x0a, 0x1d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x61, 0x73, 0x68, 0x45, 0x78, + 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, + 0x61, 0x73, 0x68, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x64, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65, 0x73, 0x22, 0xf6, 0x01, 0x0a, 0x10, 0x43, 0x61, 0x70, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x32, 0x0a, 0x0e, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x4d, 0x61, 0x70, 0x52, + 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4f, + 0x0a, 0x15, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x54, 0x72, 0x69, 0x67, 0x67, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x13, 0x72, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x4c, 0x0a, 0x14, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, + 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x0f, 0x0a, + 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x42, + 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6d, 0x61, + 0x72, 0x74, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6b, 0x69, 0x74, 0x2f, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x6b, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2f, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -225,24 +300,26 @@ func file_capabilities_pb_registry_proto_rawDescGZIP() []byte { return file_capabilities_pb_registry_proto_rawDescData } -var file_capabilities_pb_registry_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_capabilities_pb_registry_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_capabilities_pb_registry_proto_goTypes = []interface{}{ (*RemoteTriggerConfig)(nil), // 0: loop.RemoteTriggerConfig - (*CapabilityConfig)(nil), // 1: loop.CapabilityConfig - (*durationpb.Duration)(nil), // 2: google.protobuf.Duration - (*pb.Map)(nil), // 3: values.Map + (*RemoteTargetConfig)(nil), // 1: loop.RemoteTargetConfig + (*CapabilityConfig)(nil), // 2: loop.CapabilityConfig + (*durationpb.Duration)(nil), // 3: google.protobuf.Duration + (*pb.Map)(nil), // 4: values.Map } var file_capabilities_pb_registry_proto_depIdxs = []int32{ - 2, // 0: loop.RemoteTriggerConfig.registrationRefresh:type_name -> google.protobuf.Duration - 2, // 1: loop.RemoteTriggerConfig.registrationExpiry:type_name -> google.protobuf.Duration - 2, // 2: loop.RemoteTriggerConfig.messageExpiry:type_name -> google.protobuf.Duration - 3, // 3: loop.CapabilityConfig.default_config:type_name -> values.Map + 3, // 0: loop.RemoteTriggerConfig.registrationRefresh:type_name -> google.protobuf.Duration + 3, // 1: loop.RemoteTriggerConfig.registrationExpiry:type_name -> google.protobuf.Duration + 3, // 2: loop.RemoteTriggerConfig.messageExpiry:type_name -> google.protobuf.Duration + 4, // 3: loop.CapabilityConfig.default_config:type_name -> values.Map 0, // 4: loop.CapabilityConfig.remote_trigger_config:type_name -> loop.RemoteTriggerConfig - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 1, // 5: loop.CapabilityConfig.remote_target_config:type_name -> loop.RemoteTargetConfig + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_capabilities_pb_registry_proto_init() } @@ -264,6 +341,18 @@ func file_capabilities_pb_registry_proto_init() { } } file_capabilities_pb_registry_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RemoteTargetConfig); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_capabilities_pb_registry_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CapabilityConfig); i { case 0: return &v.state @@ -276,8 +365,9 @@ func file_capabilities_pb_registry_proto_init() { } } } - file_capabilities_pb_registry_proto_msgTypes[1].OneofWrappers = []interface{}{ + file_capabilities_pb_registry_proto_msgTypes[2].OneofWrappers = []interface{}{ (*CapabilityConfig_RemoteTriggerConfig)(nil), + (*CapabilityConfig_RemoteTargetConfig)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -285,7 +375,7 @@ func file_capabilities_pb_registry_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_capabilities_pb_registry_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/capabilities/pb/registry.proto b/pkg/capabilities/pb/registry.proto index 94fb80fcf..879d339c7 100644 --- a/pkg/capabilities/pb/registry.proto +++ b/pkg/capabilities/pb/registry.proto @@ -14,11 +14,19 @@ message RemoteTriggerConfig { google.protobuf.Duration messageExpiry = 4; } +message RemoteTargetConfig { + // A collection of dot seperated paths to attributes that should be excluded from the request sent to the remote target + // when calculating the hash of the request. This is useful for excluding attributes that are not deterministic to ensure + // that the hash of logically identical requests is consistent. + repeated string requestHashExcludedAttributes = 1; +} + message CapabilityConfig { values.Map default_config = 1; oneof remote_config { RemoteTriggerConfig remote_trigger_config = 2; + RemoteTargetConfig remote_target_config = 3; } } diff --git a/pkg/loop/internal/core/services/capability/capabilities_registry.go b/pkg/loop/internal/core/services/capability/capabilities_registry.go index 9e547de6a..edd9b8610 100644 --- a/pkg/loop/internal/core/services/capability/capabilities_registry.go +++ b/pkg/loop/internal/core/services/capability/capabilities_registry.go @@ -92,19 +92,27 @@ func (cr *capabilitiesRegistryClient) ConfigForCapability(ctx context.Context, c return capabilities.CapabilityConfiguration{}, fmt.Errorf("could not convert map valueproto to map: %w", err) } - var rtc capabilities.RemoteTriggerConfig - rtc.ApplyDefaults() - - if prtc := res.CapabilityConfig.GetRemoteTriggerConfig(); prtc != nil { - rtc.RegistrationRefresh = prtc.RegistrationRefresh.AsDuration() - rtc.RegistrationExpiry = prtc.RegistrationExpiry.AsDuration() - rtc.MinResponsesToAggregate = prtc.MinResponsesToAggregate - rtc.MessageExpiry = prtc.MessageExpiry.AsDuration() + var remoteTriggerConfig *capabilities.RemoteTriggerConfig + var remoteTargetConfig *capabilities.RemoteTargetConfig + + switch res.CapabilityConfig.RemoteConfig.(type) { + case *capabilitiespb.CapabilityConfig_RemoteTriggerConfig: + prtc := res.CapabilityConfig.GetRemoteTriggerConfig() + remoteTriggerConfig = &capabilities.RemoteTriggerConfig{} + remoteTriggerConfig.RegistrationRefresh = prtc.RegistrationRefresh.AsDuration() + remoteTriggerConfig.RegistrationExpiry = prtc.RegistrationExpiry.AsDuration() + remoteTriggerConfig.MinResponsesToAggregate = prtc.MinResponsesToAggregate + remoteTriggerConfig.MessageExpiry = prtc.MessageExpiry.AsDuration() + case *capabilitiespb.CapabilityConfig_RemoteTargetConfig: + prtc := res.CapabilityConfig.GetRemoteTargetConfig() + remoteTargetConfig = &capabilities.RemoteTargetConfig{} + remoteTargetConfig.RequestHashExcludedAttributes = prtc.RequestHashExcludedAttributes } return capabilities.CapabilityConfiguration{ DefaultConfig: mc, - RemoteTriggerConfig: rtc, + RemoteTriggerConfig: remoteTriggerConfig, + RemoteTargetConfig: remoteTargetConfig, }, nil } @@ -299,14 +307,25 @@ func (c *capabilitiesRegistryServer) ConfigForCapability(ctx context.Context, re ccp := &capabilitiespb.CapabilityConfig{ DefaultConfig: ecm, - RemoteConfig: &capabilitiespb.CapabilityConfig_RemoteTriggerConfig{ + } + + if cc.RemoteTriggerConfig != nil { + ccp.RemoteConfig = &capabilitiespb.CapabilityConfig_RemoteTriggerConfig{ RemoteTriggerConfig: &capabilitiespb.RemoteTriggerConfig{ RegistrationRefresh: durationpb.New(cc.RemoteTriggerConfig.RegistrationRefresh), RegistrationExpiry: durationpb.New(cc.RemoteTriggerConfig.RegistrationExpiry), MinResponsesToAggregate: cc.RemoteTriggerConfig.MinResponsesToAggregate, MessageExpiry: durationpb.New(cc.RemoteTriggerConfig.MessageExpiry), }, - }, + } + } + + if cc.RemoteTargetConfig != nil { + ccp.RemoteConfig = &capabilitiespb.CapabilityConfig_RemoteTargetConfig{ + RemoteTargetConfig: &capabilitiespb.RemoteTargetConfig{ + RequestHashExcludedAttributes: cc.RemoteTargetConfig.RequestHashExcludedAttributes, + }, + } } return &pb.ConfigForCapabilityReply{ diff --git a/pkg/loop/internal/core/services/capability/capabilities_registry_test.go b/pkg/loop/internal/core/services/capability/capabilities_registry_test.go index addaa998b..7ced20f86 100644 --- a/pkg/loop/internal/core/services/capability/capabilities_registry_test.go +++ b/pkg/loop/internal/core/services/capability/capabilities_registry_test.go @@ -430,7 +430,7 @@ func TestCapabilitiesRegistry_ConfigForCapabilities(t *testing.T) { rtc.ApplyDefaults() expectedCapConfig := capabilities.CapabilityConfiguration{ DefaultConfig: wm, - RemoteTriggerConfig: rtc, + RemoteTriggerConfig: &rtc, } reg.On("ConfigForCapability", mock.Anything, capID, donID).Once().Return(expectedCapConfig, nil) diff --git a/pkg/loop/internal/pb/chain_reader.pb.go b/pkg/loop/internal/pb/chain_reader.pb.go index 4f7b84491..4f56656da 100644 --- a/pkg/loop/internal/pb/chain_reader.pb.go +++ b/pkg/loop/internal/pb/chain_reader.pb.go @@ -1500,7 +1500,7 @@ type Block struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - BlockNumber uint64 `protobuf:"varint,1,opt,name=block_number,json=blockNumber,proto3" json:"block_number,omitempty"` + BlockNumber string `protobuf:"bytes,1,opt,name=block_number,json=blockNumber,proto3" json:"block_number,omitempty"` Operator ComparisonOperator `protobuf:"varint,2,opt,name=operator,proto3,enum=loop.ComparisonOperator" json:"operator,omitempty"` } @@ -1536,11 +1536,11 @@ func (*Block) Descriptor() ([]byte, []int) { return file_chain_reader_proto_rawDescGZIP(), []int{21} } -func (x *Block) GetBlockNumber() uint64 { +func (x *Block) GetBlockNumber() string { if x != nil { return x.BlockNumber } - return 0 + return "" } func (x *Block) GetOperator() ComparisonOperator { @@ -2101,7 +2101,7 @@ var file_chain_reader_proto_rawDesc = []byte{ 0x72, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x22, 0x60, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x28, 0x09, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x6c, 0x6f, 0x6f, 0x70, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x69, 0x73, 0x6f, 0x6e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x08, 0x6f, 0x70, 0x65, diff --git a/pkg/loop/internal/pb/chain_reader.proto b/pkg/loop/internal/pb/chain_reader.proto index 2c61141e8..c84e12620 100644 --- a/pkg/loop/internal/pb/chain_reader.proto +++ b/pkg/loop/internal/pb/chain_reader.proto @@ -151,7 +151,7 @@ message Comparator { } message Block{ - uint64 block_number = 1; + string block_number = 1; ComparisonOperator operator = 2; } diff --git a/pkg/loop/internal/relayer/pluginprovider/chainreader/helper_test.go b/pkg/loop/internal/relayer/pluginprovider/chainreader/helper_test.go index 6928e0e50..a38b87eb0 100644 --- a/pkg/loop/internal/relayer/pluginprovider/chainreader/helper_test.go +++ b/pkg/loop/internal/relayer/pluginprovider/chainreader/helper_test.go @@ -106,7 +106,7 @@ func generateQueryFilterTestCases(t *testing.T) []query.KeyFilter { primitiveExpressions := []query.Expression{query.TxHash("txHash")} for _, op := range operatorValues { - primitiveExpressions = append(primitiveExpressions, query.Block(123, op)) + primitiveExpressions = append(primitiveExpressions, query.Block("123", op)) primitiveExpressions = append(primitiveExpressions, query.Timestamp(123, op)) var valueComparators []primitives.ValueComparator diff --git a/pkg/types/query/primitives/primitives.go b/pkg/types/query/primitives/primitives.go index c18b608c5..34d26d352 100644 --- a/pkg/types/query/primitives/primitives.go +++ b/pkg/types/query/primitives/primitives.go @@ -42,7 +42,7 @@ func (f *Comparator) Accept(visitor Visitor) { // Block is a primitive of KeyFilter that filters search in comparison to block number. type Block struct { - Block uint64 + Block string Operator ComparisonOperator } diff --git a/pkg/types/query/query.go b/pkg/types/query/query.go index 1246a0bd7..129af6760 100644 --- a/pkg/types/query/query.go +++ b/pkg/types/query/query.go @@ -57,7 +57,7 @@ func Comparator(name string, valueComparators ...primitives.ValueComparator) Exp return Expression{Primitive: &primitives.Comparator{Name: name, ValueComparators: valueComparators}} } -func Block(block uint64, operator primitives.ComparisonOperator) Expression { +func Block(block string, operator primitives.ComparisonOperator) Expression { return Expression{ Primitive: &primitives.Block{Block: block, Operator: operator}, } diff --git a/pkg/types/query/query_test.go b/pkg/types/query/query_test.go index 7773c36a5..3ed3e4f1b 100644 --- a/pkg/types/query/query_test.go +++ b/pkg/types/query/query_test.go @@ -39,20 +39,20 @@ func Test_AndOrEdgeCases(t *testing.T) { }, { name: "And with multiple expressions", - expressions: []Expression{TxHash("txHash"), Block(123, primitives.Eq)}, + expressions: []Expression{TxHash("txHash"), Block("123", primitives.Eq)}, constructor: And, expected: And( TxHash("txHash"), - Block(123, primitives.Eq), + Block("123", primitives.Eq), ), }, { name: "Or with multiple expressions", - expressions: []Expression{TxHash("txHash"), Block(123, primitives.Eq)}, + expressions: []Expression{TxHash("txHash"), Block("123", primitives.Eq)}, constructor: Or, expected: Or( TxHash("txHash"), - Block(123, primitives.Eq), + Block("123", primitives.Eq), ), }, } diff --git a/pkg/values/big_int.go b/pkg/values/big_int.go index 0a891a100..81c660896 100644 --- a/pkg/values/big_int.go +++ b/pkg/values/big_int.go @@ -1,6 +1,7 @@ package values import ( + "errors" "fmt" "math/big" @@ -23,10 +24,15 @@ func (b *BigInt) proto() *pb.Value { } func (b *BigInt) Unwrap() (any, error) { - return b.Underlying, nil + bi := &big.Int{} + return bi, b.UnwrapTo(bi) } func (b *BigInt) UnwrapTo(to any) error { + if b == nil || b.Underlying == nil { + return errors.New("could not unwrap nil values.BigInt") + } + switch tb := to.(type) { case *big.Int: if tb == nil { @@ -46,6 +52,10 @@ func (b *BigInt) UnwrapTo(to any) error { } func (b *BigInt) Copy() Value { + if b == nil { + return nil + } + nw := new(big.Int) nw.Set(b.Underlying) return &BigInt{Underlying: nw} diff --git a/pkg/values/big_int_test.go b/pkg/values/big_int_test.go index 90deacfd1..e2e5c8ef5 100644 --- a/pkg/values/big_int_test.go +++ b/pkg/values/big_int_test.go @@ -30,6 +30,14 @@ func Test_BigIntUnwrapTo(t *testing.T) { var varStr string err = v.UnwrapTo(&varStr) assert.ErrorContains(t, err, "cannot unwrap to value of type: *string") + + nilVal := (*BigInt)(nil) + _, err = nilVal.Unwrap() + assert.ErrorContains(t, err, "could not unwrap nil") + + bi := &BigInt{} + _, err = bi.Unwrap() + assert.ErrorContains(t, err, "could not unwrap nil") } func Test_BigInt(t *testing.T) { diff --git a/pkg/values/bool.go b/pkg/values/bool.go index 9e44a2ac0..cf9f35f51 100644 --- a/pkg/values/bool.go +++ b/pkg/values/bool.go @@ -1,6 +1,8 @@ package values import ( + "errors" + "github.com/smartcontractkit/chainlink-common/pkg/values/pb" ) @@ -17,13 +19,20 @@ func (b *Bool) proto() *pb.Value { } func (b *Bool) Unwrap() (any, error) { - return b.Underlying, nil + var bl bool + return bl, b.UnwrapTo(&bl) } func (b *Bool) UnwrapTo(to any) error { - return unwrapTo[bool](b.Underlying, to) + if b == nil { + return errors.New("could not unwrap nil values.Bool") + } + return unwrapTo(b.Underlying, to) } func (b *Bool) Copy() Value { + if b == nil { + return nil + } return &Bool{Underlying: b.Underlying} } diff --git a/pkg/values/bool_test.go b/pkg/values/bool_test.go index 8e6050747..1512e659d 100644 --- a/pkg/values/bool_test.go +++ b/pkg/values/bool_test.go @@ -36,4 +36,12 @@ func Test_BoolUnwrapTo(t *testing.T) { err = fa.UnwrapTo(&varAny) require.NoError(t, err) assert.False(t, varAny.(bool)) + + nb := (*Bool)(nil) + _, err = nb.Unwrap() + assert.ErrorContains(t, err, "could not unwrap nil") + + var b bool + err = nb.UnwrapTo(&b) + assert.ErrorContains(t, err, "could not unwrap nil") } diff --git a/pkg/values/bytes.go b/pkg/values/bytes.go index 7ce1d548c..0414e4d6d 100644 --- a/pkg/values/bytes.go +++ b/pkg/values/bytes.go @@ -1,6 +1,8 @@ package values import ( + "errors" + "github.com/smartcontractkit/chainlink-common/pkg/values/pb" ) @@ -17,14 +19,24 @@ func (b *Bytes) proto() *pb.Value { } func (b *Bytes) Unwrap() (any, error) { + if b == nil { + return nil, errors.New("cannot unwrap nil values.Bytes") + } return b.Underlying, nil } func (b *Bytes) UnwrapTo(to any) error { + if b == nil { + return errors.New("cannot unwrap nil values.Bytes") + } return unwrapTo(b.Underlying, to) } func (b *Bytes) Copy() Value { + if b == nil { + return nil + } + dest := make([]byte, len(b.Underlying)) copy(dest, b.Underlying) return &Bytes{Underlying: dest} diff --git a/pkg/values/bytes_test.go b/pkg/values/bytes_test.go index a11328b60..9dddc2cee 100644 --- a/pkg/values/bytes_test.go +++ b/pkg/values/bytes_test.go @@ -29,4 +29,17 @@ func Test_BytesUnwrapTo(t *testing.T) { err = tr.UnwrapTo(&varAny) require.NoError(t, err) assert.Equal(t, hs, varAny) + + bn := (*Bytes)(nil) + _, err = bn.Unwrap() + assert.ErrorContains(t, err, "cannot unwrap nil") + + var bp []byte + err = bn.UnwrapTo(bp) + assert.ErrorContains(t, err, "cannot unwrap nil") + + bn = &Bytes{} + err = bn.UnwrapTo(&bp) + require.NoError(t, err) + assert.Nil(t, bp) } diff --git a/pkg/values/decimal.go b/pkg/values/decimal.go index 833da6ebd..f371bbc8a 100644 --- a/pkg/values/decimal.go +++ b/pkg/values/decimal.go @@ -1,6 +1,8 @@ package values import ( + "errors" + "github.com/shopspring/decimal" "github.com/smartcontractkit/chainlink-common/pkg/values/pb" @@ -19,13 +21,20 @@ func (d *Decimal) proto() *pb.Value { } func (d *Decimal) Unwrap() (any, error) { - return d.Underlying, nil + var dec decimal.Decimal + return dec, d.UnwrapTo(&dec) } func (d *Decimal) UnwrapTo(to any) error { + if d == nil { + return errors.New("could not unwrap nil values.Decimal") + } return unwrapTo(d.Underlying, to) } func (d *Decimal) Copy() Value { + if d == nil { + return nil + } return &Decimal{Underlying: d.Underlying.Copy()} } diff --git a/pkg/values/decimal_test.go b/pkg/values/decimal_test.go index 30b7983a6..39c7f0cc2 100644 --- a/pkg/values/decimal_test.go +++ b/pkg/values/decimal_test.go @@ -30,4 +30,12 @@ func Test_DecimalUnwrapTo(t *testing.T) { err = tr.UnwrapTo(&varAny) require.NoError(t, err) assert.Equal(t, dv, varAny) + + dn := (*Decimal)(nil) + _, err = dn.Unwrap() + assert.ErrorContains(t, err, "could not unwrap nil") + + dec = decimal.Decimal{} + err = dn.UnwrapTo(&dec) + assert.ErrorContains(t, err, "could not unwrap nil") } diff --git a/pkg/values/int.go b/pkg/values/int.go index b2f70944e..96ef1f960 100644 --- a/pkg/values/int.go +++ b/pkg/values/int.go @@ -1,6 +1,7 @@ package values import ( + "errors" "fmt" "math" @@ -20,14 +21,22 @@ func (i *Int64) proto() *pb.Value { } func (i *Int64) Unwrap() (any, error) { - return i.Underlying, nil + var u int64 + return u, i.UnwrapTo(&u) } func (i *Int64) Copy() Value { + if i == nil { + return nil + } return &Int64{Underlying: i.Underlying} } func (i *Int64) UnwrapTo(to any) error { + if i == nil { + return errors.New("cannot unwrap nil values.Int64") + } + if to == nil { return fmt.Errorf("cannot unwrap to nil pointer: %+v", to) } diff --git a/pkg/values/int_test.go b/pkg/values/int_test.go index deb005dbe..8cd80c60b 100644 --- a/pkg/values/int_test.go +++ b/pkg/values/int_test.go @@ -27,4 +27,12 @@ func Test_IntUnwrapTo(t *testing.T) { err = v.UnwrapTo(&varAny) require.NoError(t, err) assert.Equal(t, expected, varAny) + + in := (*Int64)(nil) + _, err = in.Unwrap() + assert.ErrorContains(t, err, "cannot unwrap nil") + + var i int64 + err = in.UnwrapTo(&i) + assert.ErrorContains(t, err, "cannot unwrap nil") } diff --git a/pkg/values/list.go b/pkg/values/list.go index 7754afc93..728f7a9c3 100644 --- a/pkg/values/list.go +++ b/pkg/values/list.go @@ -1,6 +1,7 @@ package values import ( + "errors" "fmt" "reflect" @@ -38,6 +39,10 @@ func (l *List) Unwrap() (any, error) { } func (l *List) Copy() Value { + if l == nil { + return nil + } + dest := []Value{} for _, el := range l.Underlying { dest = append(dest, el.Copy()) @@ -46,6 +51,10 @@ func (l *List) Copy() Value { } func (l *List) UnwrapTo(to any) error { + if l == nil { + return errors.New("cannot unwrap nil values.List") + } + val := reflect.ValueOf(to) if val.Kind() != reflect.Pointer { return fmt.Errorf("cannot unwrap to non-pointer type %T", to) diff --git a/pkg/values/list_test.go b/pkg/values/list_test.go index 5541bda1e..dbac6fada 100644 --- a/pkg/values/list_test.go +++ b/pkg/values/list_test.go @@ -88,6 +88,22 @@ func Test_ListUnwrapTo(t *testing.T) { require.NoError(t, err) assert.Equal(t, expected, got) }) + + t.Run("cannot unwrap a nil list", func(t *testing.T) { + l := (*List)(nil) + _, err := l.Unwrap() + assert.ErrorContains(t, err, "cannot unwrap nil") + + var a []any + err = l.UnwrapTo(&a) + assert.ErrorContains(t, err, "cannot unwrap nil") + }) + + t.Run("can unwrap an unitialized list", func(t *testing.T) { + l := &List{} + _, err := l.Unwrap() + assert.NoError(t, err) + }) } func sliceTest[T any](t *testing.T, expected []T, got []T) { diff --git a/pkg/values/map.go b/pkg/values/map.go index 3137e695f..013c53b09 100644 --- a/pkg/values/map.go +++ b/pkg/values/map.go @@ -1,7 +1,9 @@ package values import ( + "errors" "reflect" + "strings" "github.com/mitchellh/mapstructure" @@ -57,6 +59,10 @@ func (m *Map) Copy() Value { } func (m *Map) CopyMap() *Map { + if m == nil { + return nil + } + dest := map[string]Value{} for k, v := range m.Underlying { dest[k] = v.Copy() @@ -66,6 +72,10 @@ func (m *Map) CopyMap() *Map { } func (m *Map) UnwrapTo(to any) error { + if m == nil { + return errors.New("cannot unwrap nil values.Map") + } + c := &mapstructure.DecoderConfig{ Result: to, DecodeHook: mapstructure.ComposeDecodeHookFunc( @@ -82,6 +92,33 @@ func (m *Map) UnwrapTo(to any) error { return d.Decode(m.Underlying) } +// DeleteAtPath deletes a value from a map at a given dot separated path. Returns true if an element at the given +// path was found and deleted, false otherwise. +func (m *Map) DeleteAtPath(path string) bool { + pathSegments := strings.Split(path, ".") + underlying := m.Underlying + for segmentIdx, pathSegment := range pathSegments { + if segmentIdx == len(pathSegments)-1 { + _, ok := underlying[pathSegment] + if !ok { + return false + } + + delete(underlying, pathSegment) + return true + } + + value := underlying[pathSegment] + mv, ok := value.(*Map) + if !ok { + return false + } + underlying = mv.Underlying + } + + return false +} + func mapValueToMap(f reflect.Type, t reflect.Type, data any) (any, error) { if f != reflect.TypeOf(map[string]Value{}) { return data, nil diff --git a/pkg/values/map_test.go b/pkg/values/map_test.go index d7a2fcfc6..8db5488b2 100644 --- a/pkg/values/map_test.go +++ b/pkg/values/map_test.go @@ -34,6 +34,24 @@ type testStruct struct { MapValue *Map } +func TestMap_UnwrapTo_Nil(t *testing.T) { + m := (*Map)(nil) + _, err := m.Unwrap() + assert.ErrorContains(t, err, "cannot unwrap nil") + + mv := map[string]any{} + err = m.UnwrapTo(mv) + assert.ErrorContains(t, err, "cannot unwrap nil") + + m = &Map{} + _, err = m.Unwrap() + assert.NoError(t, err) + + m = &Map{} + err = m.UnwrapTo(&mv) + assert.NoError(t, err) +} + func TestMap_UnwrapTo(t *testing.T) { im := map[string]any{ "foo": "bar", @@ -228,3 +246,35 @@ func TestMap_UnwrapTo_OtherMapTypes(t *testing.T) { }) } } + +func Test_DeleteAtPath(t *testing.T) { + im := map[string]any{ + "foo": map[string]any{"bar": map[string]any{"baz": "caz"}}, + "roo": map[string]any{"rar": map[string]any{"raz": "taz"}}, + } + wrappedMap, err := NewMap(im) + require.NoError(t, err) + assert.NotNil(t, wrappedMap.Underlying["foo"].(*Map).Underlying["bar"]) + + deleted := wrappedMap.DeleteAtPath("") + assert.Falsef(t, deleted, "expected to not delete empty path") + + deleted = wrappedMap.DeleteAtPath("foo.bah") + assert.Falsef(t, deleted, "expected to not delete key foo.bah") + assert.NotNil(t, wrappedMap.Underlying["foo"].(*Map).Underlying["bar"]) + + deleted = wrappedMap.DeleteAtPath("foo.bar.baz") + assert.Truef(t, deleted, "expected to delete key foo.bar.baz") + + assert.NotNil(t, wrappedMap.Underlying["foo"]) + assert.NotNil(t, wrappedMap.Underlying["foo"].(*Map).Underlying["bar"]) + assert.Nil(t, wrappedMap.Underlying["foo"].(*Map).Underlying["bar"].(*Map).Underlying["bar"]) + + deleted = wrappedMap.DeleteAtPath("foo.bar.baz") + assert.Falsef(t, deleted, "expected to not delete key foo.bar.baz") + + deleted = wrappedMap.DeleteAtPath("foo.bar") + assert.Truef(t, deleted, "expected to delete key foo.bar") + assert.Nil(t, wrappedMap.Underlying["foo"].(*Map).Underlying["bar"]) + +} diff --git a/pkg/values/string.go b/pkg/values/string.go index a913d1a18..d51929318 100644 --- a/pkg/values/string.go +++ b/pkg/values/string.go @@ -1,6 +1,8 @@ package values import ( + "errors" + "github.com/smartcontractkit/chainlink-common/pkg/values/pb" ) @@ -17,13 +19,20 @@ func (s *String) proto() *pb.Value { } func (s *String) Unwrap() (any, error) { - return s.Underlying, nil + var to string + return to, s.UnwrapTo(&to) } func (s *String) UnwrapTo(to any) error { + if s == nil { + return errors.New("cannot unwrap nil values.String") + } return unwrapTo(s.Underlying, to) } func (s *String) Copy() Value { + if s == nil { + return s + } return &String{Underlying: s.Underlying} } diff --git a/pkg/values/string_test.go b/pkg/values/string_test.go index ef3ca5200..4c045327a 100644 --- a/pkg/values/string_test.go +++ b/pkg/values/string_test.go @@ -25,4 +25,12 @@ func Test_StringUnwrapTo(t *testing.T) { err = v.UnwrapTo(&varAny) require.NoError(t, err) assert.Equal(t, expected, varAny) + + sn := (*String)(nil) + _, err = sn.Unwrap() + assert.ErrorContains(t, err, "cannot unwrap nil") + + var s string + err = sn.UnwrapTo(&s) + assert.ErrorContains(t, err, "cannot unwrap nil") } diff --git a/pkg/values/value_test.go b/pkg/values/value_test.go index c2a4ca40b..28bdc8545 100644 --- a/pkg/values/value_test.go +++ b/pkg/values/value_test.go @@ -260,6 +260,7 @@ func Test_Copy(t *testing.T) { tcs := []struct { value Value + isNil bool }{ { value: NewString("hello"), @@ -285,10 +286,42 @@ func Test_Copy(t *testing.T) { { value: mp, }, + { + value: (*String)(nil), + isNil: true, + }, + { + value: (*Bytes)(nil), + isNil: true, + }, + { + value: (*Int64)(nil), + isNil: true, + }, + { + value: (*BigInt)(nil), + isNil: true, + }, + { + value: (*Bool)(nil), + isNil: true, + }, + { + value: (*List)(nil), + isNil: true, + }, + { + value: (*Map)(nil), + isNil: true, + }, } for _, tc := range tcs { copied := tc.value.Copy() - assert.Equal(t, tc.value, copied) + if tc.isNil { + assert.Nil(t, tc.value.Copy()) + } else { + assert.Equal(t, tc.value, copied) + } } }