Skip to content

Commit

Permalink
fix: binding of 3th level fields
Browse files Browse the repository at this point in the history
  • Loading branch information
JulianToledano committed Dec 17, 2024
1 parent 7e97cbb commit abed3e2
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 25 deletions.
38 changes: 24 additions & 14 deletions client/v2/autocli/flag/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,13 @@ func (b *Builder) addMessageFlags(ctx *context.Context, flagSet *pflag.FlagSet,

s := strings.Split(arg.ProtoField, ".")
if len(s) == 1 {
err := b.addFieldBindingToArgs(ctx, messageBinder, protoreflect.Name(arg.ProtoField), fields)
f, err := b.addFieldBindingToArgs(ctx, messageBinder, protoreflect.Name(arg.ProtoField), fields)
if err != nil {
return nil, err
}
messageBinder.positionalArgs = append(messageBinder.positionalArgs, f)
} else {
err := b.addFlattenFieldBindingToArgs(ctx, s, messageType, messageBinder)
err := b.addFlattenFieldBindingToArgs(ctx, arg.ProtoField, s, messageType, messageBinder)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -271,22 +272,33 @@ func (b *Builder) addMessageFlags(ctx *context.Context, flagSet *pflag.FlagSet,
// It takes a slice of field names representing the path to the target field, where each element is a field name
// in the nested message structure. For example, ["foo", "bar", "baz"] would bind the "baz" field inside the "bar"
// message which is inside the "foo" message.
func (b *Builder) addFlattenFieldBindingToArgs(ctx *context.Context, s []string, msg protoreflect.MessageType, messageBinder *MessageBinder) error {
func (b *Builder) addFlattenFieldBindingToArgs(ctx *context.Context, path string, s []string, msg protoreflect.MessageType, messageBinder *MessageBinder) error {
fields := msg.Descriptor().Fields()
if len(s) == 1 {
return b.addFieldBindingToArgs(ctx, messageBinder, protoreflect.Name(s[0]), fields)
f, err := b.addFieldBindingToArgs(ctx, messageBinder, protoreflect.Name(s[0]), fields)
if err != nil {
return err
}
f.path = path
messageBinder.positionalArgs = append(messageBinder.positionalArgs, f)
return nil
}

innerMsg := msg.New().Get(fields.ByName(protoreflect.Name(s[0]))).Message().Type()
return b.addFlattenFieldBindingToArgs(ctx, s[1:], innerMsg, messageBinder)
fd := fields.ByName(protoreflect.Name(s[0]))
var innerMsg protoreflect.MessageType
if fd.IsList() {
innerMsg = msg.New().Get(fd).List().NewElement().Message().Type()
} else {
innerMsg = msg.New().Get(fd).Message().Type()
}
return b.addFlattenFieldBindingToArgs(ctx, path, s[1:], innerMsg, messageBinder)
}

// addFieldBindingToArgs adds a fieldBinding for a positional argument to the message binder.
// The fieldBinding is appended to the positional arguments list in the message binder.
func (b *Builder) addFieldBindingToArgs(ctx *context.Context, messageBinder *MessageBinder, name protoreflect.Name, fields protoreflect.FieldDescriptors) error {
func (b *Builder) addFieldBindingToArgs(ctx *context.Context, messageBinder *MessageBinder, name protoreflect.Name, fields protoreflect.FieldDescriptors) (fieldBinding, error) {
field := fields.ByName(name)
if field == nil {
return fmt.Errorf("can't find field %s", name) // TODO: it will improve error if msg.FullName() was included.`
return fieldBinding{}, fmt.Errorf("can't find field %s", name) // TODO: it will improve error if msg.FullName() was included.`
}

_, hasValue, err := b.addFieldFlag(
Expand All @@ -297,15 +309,13 @@ func (b *Builder) addFieldBindingToArgs(ctx *context.Context, messageBinder *Mes
namingOptions{},
)
if err != nil {
return err
return fieldBinding{}, err
}

messageBinder.positionalArgs = append(messageBinder.positionalArgs, fieldBinding{
return fieldBinding{
field: field,
hasValue: hasValue,
})

return nil
}, nil
}

// bindPageRequest create a flag for pagination
Expand Down
39 changes: 28 additions & 11 deletions client/v2/autocli/flag/messager_binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ func (m MessageBinder) Bind(msg protoreflect.Message, positionalArgs []string) e
return err
}
} else {
if err := m.bindNestedField(msg, arg); err != nil {
s := strings.Split(arg.path, ".")
if err := m.bindNestedField(msg, arg, s); err != nil {
return err
}
}
Expand All @@ -92,20 +93,35 @@ func (m MessageBinder) Bind(msg protoreflect.Message, positionalArgs []string) e

// bindNestedField binds a field value to a nested message field. It handles cases where the field
// belongs to a nested message type by recursively traversing the message structure.
func (m *MessageBinder) bindNestedField(msg protoreflect.Message, arg fieldBinding) error {
name := protoreflect.Name(strings.ToLower(string(arg.field.Parent().Name())))
innerMsgValue := msg.Get(msg.Descriptor().Fields().ByName(name))
if !innerMsgValue.Message().IsValid() {
msg.Set(msg.Descriptor().Fields().ByName(name), protoreflect.ValueOfMessage(innerMsgValue.Message().New()))
func (m *MessageBinder) bindNestedField(msg protoreflect.Message, arg fieldBinding, path []string) error {
if len(path) == 1 {
return arg.bind(msg)
}

innerMsg := msg.Get(msg.Descriptor().Fields().ByName(name)).Message()
argField := innerMsg.Descriptor().Fields().ByName(arg.field.Name())
if argField.Kind() == protoreflect.MessageKind {
return m.bindNestedField(innerMsg, arg)
name := protoreflect.Name(path[0])
fd := msg.Descriptor().Fields().ByName(name)
if fd == nil {
return fmt.Errorf("field %q not found", path[0])
}

return arg.bind(innerMsg)
var innerMsg protoreflect.Message
if fd.IsList() {
if msg.Get(fd).List().Len() == 0 {
l := msg.Mutable(fd).List()
elem := l.NewElement().Message().New()
l.Append(protoreflect.ValueOfMessage(elem))
msg.Set(msg.Descriptor().Fields().ByName(name), protoreflect.ValueOfList(l))
}
innerMsg = msg.Get(fd).List().Get(0).Message()
} else {
innerMsgValue := msg.Get(fd)
if !innerMsgValue.Message().IsValid() {
msg.Set(msg.Descriptor().Fields().ByName(name), protoreflect.ValueOfMessage(innerMsgValue.Message().New()))
}
innerMsg = msg.Get(msg.Descriptor().Fields().ByName(name)).Message()
}

return m.bindNestedField(innerMsg, arg, path[1:])
}

// Get calls BuildMessage and wraps the result in a protoreflect.Value.
Expand All @@ -117,6 +133,7 @@ func (m MessageBinder) Get(protoreflect.Value) (protoreflect.Value, error) {
type fieldBinding struct {
hasValue HasValue
field protoreflect.FieldDescriptor
path string
}

func (f fieldBinding) bind(msg protoreflect.Message) error {
Expand Down
28 changes: 28 additions & 0 deletions client/v2/autocli/msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
"cosmossdk.io/client/v2/internal/testpb"
_ "cosmossdk.io/client/v2/internal/testpb"

"github.com/cosmos/cosmos-sdk/client"
)
Expand Down Expand Up @@ -128,6 +129,33 @@ func TestMsg(t *testing.T) {
assertNormalizedJSONEqual(t, out.Bytes(), goldenLoad(t, "msg-output.golden"))
}

func TestMsgWithFlattenFields(t *testing.T) {
fixture := initFixture(t)

out, err := runCmd(fixture, buildCustomModuleMsgCommand(&autocliv1.ServiceCommandDescriptor{
Service: bankv1beta1.Msg_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "UpdateParams",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{
{ProtoField: "authority"},
{ProtoField: "params.send_enabled.denom"},
{ProtoField: "params.send_enabled.enabled"},
{ProtoField: "params.default_send_enabled"},
},
},
},
EnhanceCustomCommand: true,
}), "update-params",
"cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk", "stake", "true", "true",
"--generate-only",
"--output", "json",
"--chain-id", fixture.chainID,
)
assert.NilError(t, err)
assertNormalizedJSONEqual(t, out.Bytes(), goldenLoad(t, "flatten-output.golden"))
}

func goldenLoad(t *testing.T, filename string) []byte {
t.Helper()
content, err := os.ReadFile(filepath.Join("testdata", filename))
Expand Down
1 change: 1 addition & 0 deletions client/v2/autocli/testdata/flatten-output.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"body":{"messages":[{"@type":"/cosmos.bank.v1beta1.MsgUpdateParams","authority":"cosmos1y74p8wyy4enfhfn342njve6cjmj5c8dtl6emdk","params":{"send_enabled":[{"denom":"stake","enabled":true}],"default_send_enabled":true}}],"memo":"","timeout_height":"0","unordered":false,"timeout_timestamp":"1970-01-01T00:00:00Z","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""},"tip":null},"signatures":[]}

0 comments on commit abed3e2

Please sign in to comment.