refactor: Improve textual's API (#14595)
This commit is contained in:
parent
5e7b744592
commit
305053136a
117
api/cosmos/msg/textual/v1/textual.pulsar.go
Normal file
117
api/cosmos/msg/textual/v1/textual.pulsar.go
Normal file
@ -0,0 +1,117 @@
|
||||
// Code generated by protoc-gen-go-pulsar. DO NOT EDIT.
|
||||
package textualv1
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.0
|
||||
// protoc (unknown)
|
||||
// source: cosmos/msg/textual/v1/textual.proto
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
var file_cosmos_msg_textual_v1_textual_proto_extTypes = []protoimpl.ExtensionInfo{
|
||||
{
|
||||
ExtendedType: (*descriptorpb.MessageOptions)(nil),
|
||||
ExtensionType: (*string)(nil),
|
||||
Field: 11110009,
|
||||
Name: "cosmos.msg.textual.v1.expert_custom_renderer",
|
||||
Tag: "bytes,11110009,opt,name=expert_custom_renderer",
|
||||
Filename: "cosmos/msg/textual/v1/textual.proto",
|
||||
},
|
||||
}
|
||||
|
||||
// Extension fields to descriptorpb.MessageOptions.
|
||||
var (
|
||||
// expert_custom_renderer is an informative identifier to reference the
|
||||
// algorithm used to generate the custom textual representation of the
|
||||
// protobuf message where this annotation is applied. We recommend to use a
|
||||
// short, versioned name as this identifier, e.g. "replace_with_username_v1".
|
||||
// We also recommand providing a human-readable description as protobuf
|
||||
// comments on this annotation, for example a short specification or a link
|
||||
// to the relevant documentation.
|
||||
//
|
||||
// Also see the section on Custom Message Renderers in ADR-050.
|
||||
//
|
||||
// optional string expert_custom_renderer = 11110009;
|
||||
E_ExpertCustomRenderer = &file_cosmos_msg_textual_v1_textual_proto_extTypes[0]
|
||||
)
|
||||
|
||||
var File_cosmos_msg_textual_v1_textual_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_cosmos_msg_textual_v1_textual_proto_rawDesc = []byte{
|
||||
0x0a, 0x23, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, 0x74, 0x65, 0x78,
|
||||
0x74, 0x75, 0x61, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x6d, 0x73,
|
||||
0x67, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x1a, 0x20, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65,
|
||||
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3a, 0x58,
|
||||
0x0a, 0x16, 0x65, 0x78, 0x70, 0x65, 0x72, 0x74, 0x5f, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f,
|
||||
0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
|
||||
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xf9, 0x8c, 0xa6, 0x05, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x14, 0x65, 0x78, 0x70, 0x65, 0x72, 0x74, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d,
|
||||
0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x42, 0xd2, 0x01, 0x0a, 0x19, 0x63, 0x6f, 0x6d,
|
||||
0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x6d, 0x73, 0x67, 0x2e, 0x74, 0x65, 0x78, 0x74,
|
||||
0x75, 0x61, 0x6c, 0x2e, 0x76, 0x31, 0x42, 0x0c, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x50,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64,
|
||||
0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f,
|
||||
0x6d, 0x73, 0x67, 0x2f, 0x74, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x2f, 0x76, 0x31, 0x3b, 0x74,
|
||||
0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x4d, 0x54, 0xaa, 0x02,
|
||||
0x15, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x4d, 0x73, 0x67, 0x2e, 0x54, 0x65, 0x78, 0x74,
|
||||
0x75, 0x61, 0x6c, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x15, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c,
|
||||
0x4d, 0x73, 0x67, 0x5c, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x5c, 0x56, 0x31, 0xe2, 0x02,
|
||||
0x21, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x4d, 0x73, 0x67, 0x5c, 0x54, 0x65, 0x78, 0x74,
|
||||
0x75, 0x61, 0x6c, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||
0x74, 0x61, 0xea, 0x02, 0x18, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x4d, 0x73, 0x67,
|
||||
0x3a, 0x3a, 0x54, 0x65, 0x78, 0x74, 0x75, 0x61, 0x6c, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var file_cosmos_msg_textual_v1_textual_proto_goTypes = []interface{}{
|
||||
(*descriptorpb.MessageOptions)(nil), // 0: google.protobuf.MessageOptions
|
||||
}
|
||||
var file_cosmos_msg_textual_v1_textual_proto_depIdxs = []int32{
|
||||
0, // 0: cosmos.msg.textual.v1.expert_custom_renderer:extendee -> google.protobuf.MessageOptions
|
||||
1, // [1:1] is the sub-list for method output_type
|
||||
1, // [1:1] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
0, // [0:1] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_cosmos_msg_textual_v1_textual_proto_init() }
|
||||
func file_cosmos_msg_textual_v1_textual_proto_init() {
|
||||
if File_cosmos_msg_textual_v1_textual_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_cosmos_msg_textual_v1_textual_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 0,
|
||||
NumExtensions: 1,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_cosmos_msg_textual_v1_textual_proto_goTypes,
|
||||
DependencyIndexes: file_cosmos_msg_textual_v1_textual_proto_depIdxs,
|
||||
ExtensionInfos: file_cosmos_msg_textual_v1_textual_proto_extTypes,
|
||||
}.Build()
|
||||
File_cosmos_msg_textual_v1_textual_proto = out.File
|
||||
file_cosmos_msg_textual_v1_textual_proto_rawDesc = nil
|
||||
file_cosmos_msg_textual_v1_textual_proto_goTypes = nil
|
||||
file_cosmos_msg_textual_v1_textual_proto_depIdxs = nil
|
||||
}
|
||||
@ -228,7 +228,6 @@ message Grant {
|
||||
|
||||
message MsgGrant {
|
||||
option (cosmos.msg.v1.signer) = "granter";
|
||||
option (cosmos.msg.v1.textual.type_url) = "authz v1beta1 grant";
|
||||
|
||||
string granter = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
string grantee = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
|
||||
@ -249,13 +248,13 @@ End of transaction messages
|
||||
|
||||
Application developers may choose to not follow default renderer value output for their own `Msg`s. In this case, they can implement their own custom `Msg` renderer. This is similar to [EIP4430](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4430.md), where the smart contract developer chooses the description string to be shown to the end user.
|
||||
|
||||
This is done by setting the `cosmos.msg.v1.textual.expert_custom_renderer` Protobuf option to a non-empty string. This option CAN ONLY be set on a Protobuf message representing transaction message object (implementing `sdk.Msg` interface).
|
||||
This is done by setting the `cosmos.msg.textual.v1.expert_custom_renderer` Protobuf option to a non-empty string. This option CAN ONLY be set on a Protobuf message representing transaction message object (implementing `sdk.Msg` interface).
|
||||
|
||||
```protobuf
|
||||
message MsgFooBar {
|
||||
// Optional comments to describe in human-readable language the formatting
|
||||
// rules of the custom renderer.
|
||||
option (cosmos.msg.v1.textual.expert_custom_renderer) = "<unique algorithm identifier>";
|
||||
option (cosmos.msg.textual.v1.expert_custom_renderer) = "<unique algorithm identifier>";
|
||||
|
||||
// proto fields
|
||||
}
|
||||
|
||||
18
proto/cosmos/msg/textual/v1/textual.proto
Normal file
18
proto/cosmos/msg/textual/v1/textual.proto
Normal file
@ -0,0 +1,18 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package cosmos.msg.textual.v1;
|
||||
|
||||
import "google/protobuf/descriptor.proto";
|
||||
|
||||
extend google.protobuf.MessageOptions {
|
||||
// expert_custom_renderer is an informative identifier to reference the
|
||||
// algorithm used to generate the custom textual representation of the
|
||||
// protobuf message where this annotation is applied. We recommend to use a
|
||||
// short, versioned name as this identifier, e.g. "replace_with_username_v1".
|
||||
// We also recommand providing a human-readable description as protobuf
|
||||
// comments on this annotation, for example a short specification or a link
|
||||
// to the relevant documentation.
|
||||
//
|
||||
// Also see the section on Custom Message Renderers in ADR-050.
|
||||
string expert_custom_renderer = 11110009;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -7,8 +7,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
|
||||
"cosmossdk.io/tx/textual"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@ -20,18 +19,18 @@ import (
|
||||
|
||||
type anyJsonTest struct {
|
||||
Proto json.RawMessage
|
||||
Screens []valuerenderer.Screen
|
||||
Screens []textual.Screen
|
||||
}
|
||||
|
||||
func TestAny(t *testing.T) {
|
||||
raw, err := os.ReadFile("../internal/testdata/any.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/any.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
var testcases []anyJsonTest
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
tr := valuerenderer.NewTextual(EmptyCoinMetadataQuerier)
|
||||
tr := textual.NewTextual(EmptyCoinMetadataQuerier)
|
||||
for i, tc := range testcases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
anyMsg := anypb.Any{}
|
||||
@ -39,7 +38,7 @@ func TestAny(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Format into screens and check vs expected
|
||||
rend := valuerenderer.NewAnyValueRenderer((&tr))
|
||||
rend := textual.NewAnyValueRenderer((&tr))
|
||||
screens, err := rend.Format(context.Background(), protoreflect.ValueOfMessage(anyMsg.ProtoReflect()))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.Screens, screens)
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
"cosmossdk.io/tx/textual"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
)
|
||||
@ -15,12 +15,12 @@ func TestBytesJsonTestCases(t *testing.T) {
|
||||
var testcases []bytesTest
|
||||
// Bytes.json contains bytes that are represented in base64 format, and
|
||||
// their expected results in hex.
|
||||
raw, err := os.ReadFile("../internal/testdata/bytes.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/bytes.json")
|
||||
require.NoError(t, err)
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
textual := valuerenderer.NewTextual(nil)
|
||||
textual := textual.NewTextual(nil)
|
||||
|
||||
for _, tc := range testcases {
|
||||
valrend, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("BYTES"))
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -12,7 +12,7 @@ import (
|
||||
|
||||
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
|
||||
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
"cosmossdk.io/tx/textual"
|
||||
)
|
||||
|
||||
// mockCoinMetadataKey is used in the mock coin metadata querier.
|
||||
@ -47,33 +47,33 @@ func addMetadataToContext(ctx context.Context, metadata *bankv1beta1.Metadata) c
|
||||
|
||||
func TestMetadataQuerier(t *testing.T) {
|
||||
// Errors on nil metadata querier
|
||||
textual := valuerenderer.NewTextual(nil)
|
||||
vr, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("COIN"))
|
||||
txt := textual.NewTextual(nil)
|
||||
vr, err := txt.GetFieldValueRenderer(fieldDescriptorFromName("COIN"))
|
||||
require.NoError(t, err)
|
||||
_, err = vr.Format(context.Background(), protoreflect.ValueOf((&basev1beta1.Coin{}).ProtoReflect()))
|
||||
require.Error(t, err)
|
||||
|
||||
// Errors if metadata querier returns an error
|
||||
expErr := fmt.Errorf("mock error")
|
||||
textual = valuerenderer.NewTextual(func(_ context.Context, _ string) (*bankv1beta1.Metadata, error) {
|
||||
txt = textual.NewTextual(func(_ context.Context, _ string) (*bankv1beta1.Metadata, error) {
|
||||
return nil, expErr
|
||||
})
|
||||
vr, err = textual.GetFieldValueRenderer(fieldDescriptorFromName("COIN"))
|
||||
vr, err = txt.GetFieldValueRenderer(fieldDescriptorFromName("COIN"))
|
||||
require.NoError(t, err)
|
||||
_, err = vr.Format(context.Background(), protoreflect.ValueOf((&basev1beta1.Coin{}).ProtoReflect()))
|
||||
require.ErrorIs(t, err, expErr)
|
||||
_, err = vr.(valuerenderer.RepeatedValueRenderer).FormatRepeated(context.Background(), protoreflect.ValueOf(NewGenericList([]*basev1beta1.Coin{{}})))
|
||||
_, err = vr.(textual.RepeatedValueRenderer).FormatRepeated(context.Background(), protoreflect.ValueOf(NewGenericList([]*basev1beta1.Coin{{}})))
|
||||
require.ErrorIs(t, err, expErr)
|
||||
}
|
||||
|
||||
func TestCoinJsonTestcases(t *testing.T) {
|
||||
var testcases []coinJsonTest
|
||||
raw, err := os.ReadFile("../internal/testdata/coin.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/coin.json")
|
||||
require.NoError(t, err)
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
textual := valuerenderer.NewTextual(mockCoinMetadataQuerier)
|
||||
textual := textual.NewTextual(mockCoinMetadataQuerier)
|
||||
vr, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("COIN"))
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -9,21 +9,21 @@ import (
|
||||
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
|
||||
basev1beta1 "cosmossdk.io/api/cosmos/base/v1beta1"
|
||||
"cosmossdk.io/math"
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
"cosmossdk.io/tx/textual"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
)
|
||||
|
||||
func TestCoinsJsonTestcases(t *testing.T) {
|
||||
var testcases []coinsJsonTest
|
||||
raw, err := os.ReadFile("../internal/testdata/coins.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/coins.json")
|
||||
require.NoError(t, err)
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
textual := valuerenderer.NewTextual(mockCoinMetadataQuerier)
|
||||
vr, err := textual.GetFieldValueRenderer(fieldDescriptorFromName("COINS"))
|
||||
vrr := vr.(valuerenderer.RepeatedValueRenderer)
|
||||
txt := textual.NewTextual(mockCoinMetadataQuerier)
|
||||
vr, err := txt.GetFieldValueRenderer(fieldDescriptorFromName("COINS"))
|
||||
vrr := vr.(textual.RepeatedValueRenderer)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, tc := range testcases {
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -1,11 +1,11 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
"cosmossdk.io/tx/textual"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
)
|
||||
@ -13,12 +13,12 @@ import (
|
||||
func TestDecJsonTestcases(t *testing.T) {
|
||||
type decimalTest []string
|
||||
var testcases []decimalTest
|
||||
raw, err := os.ReadFile("../internal/testdata/decimals.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/decimals.json")
|
||||
require.NoError(t, err)
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
textual := valuerenderer.NewTextual(nil)
|
||||
textual := textual.NewTextual(nil)
|
||||
|
||||
for _, tc := range testcases {
|
||||
tc := tc
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
"cosmossdk.io/tx/textual"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
@ -22,7 +22,7 @@ type durationTest struct {
|
||||
}
|
||||
|
||||
func TestDurationJSON(t *testing.T) {
|
||||
raw, err := os.ReadFile("../internal/testdata/duration.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/duration.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
var testcases []durationTest
|
||||
@ -31,9 +31,9 @@ func TestDurationJSON(t *testing.T) {
|
||||
|
||||
for i, tc := range testcases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
rend := valuerenderer.NewDurationValueRenderer()
|
||||
rend := textual.NewDurationValueRenderer()
|
||||
|
||||
var screens []valuerenderer.Screen
|
||||
var screens []textual.Screen
|
||||
if tc.Proto != nil {
|
||||
screens, err = rend.Format(context.Background(), protoreflect.ValueOf(tc.Proto.ProtoReflect()))
|
||||
if tc.Error {
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -16,8 +16,8 @@ import (
|
||||
_ "cosmossdk.io/api/cosmos/crypto/multisig"
|
||||
_ "cosmossdk.io/api/cosmos/crypto/secp256k1"
|
||||
_ "cosmossdk.io/api/cosmos/gov/v1"
|
||||
"cosmossdk.io/tx/textual"
|
||||
"cosmossdk.io/tx/textual/internal/textualpb"
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
)
|
||||
|
||||
type e2eJsonTest struct {
|
||||
@ -26,7 +26,7 @@ type e2eJsonTest struct {
|
||||
}
|
||||
|
||||
func TestE2EJsonTestcases(t *testing.T) {
|
||||
raw, err := os.ReadFile("../internal/testdata/e2e.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/e2e.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
var testcases []e2eJsonTest
|
||||
@ -37,8 +37,8 @@ func TestE2EJsonTestcases(t *testing.T) {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
_, bodyBz, _, authInfoBz, signerData := createTextualData(t, tc.Proto, tc.SignerData)
|
||||
|
||||
tr := valuerenderer.NewTextual(mockCoinMetadataQuerier)
|
||||
rend := valuerenderer.NewTxValueRenderer(&tr)
|
||||
tr := textual.NewTextual(mockCoinMetadataQuerier)
|
||||
rend := textual.NewTxValueRenderer(&tr)
|
||||
ctx := addMetadataToContext(context.Background(), tc.Metadata)
|
||||
|
||||
data := &textualpb.TextualData{
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"io"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -17,7 +17,7 @@ type encodingJsonTest struct {
|
||||
}
|
||||
|
||||
func TestEncodingJson(t *testing.T) {
|
||||
raw, err := os.ReadFile("../internal/testdata/encode.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/encode.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
var testcases []encodingJsonTest
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -7,8 +7,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/tx/textual"
|
||||
"cosmossdk.io/tx/textual/internal/testpb"
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
@ -23,12 +23,12 @@ type enumTest struct {
|
||||
|
||||
func TestEnumJsonTestcases(t *testing.T) {
|
||||
var testcases []enumTest
|
||||
raw, err := os.ReadFile("../internal/testdata/enum.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/enum.json")
|
||||
require.NoError(t, err)
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
textual := valuerenderer.NewTextual(nil)
|
||||
textual := textual.NewTextual(nil)
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.Text, func(t *testing.T) {
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -11,18 +11,18 @@ import (
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"cosmossdk.io/math"
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
"cosmossdk.io/tx/textual"
|
||||
)
|
||||
|
||||
func TestIntJsonTestcases(t *testing.T) {
|
||||
type integerTest []string
|
||||
var testcases []integerTest
|
||||
raw, err := os.ReadFile("../internal/testdata/integers.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/integers.json")
|
||||
require.NoError(t, err)
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
textual := valuerenderer.NewTextual(nil)
|
||||
textual := textual.NewTextual(nil)
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc[0], func(t *testing.T) {
|
||||
@ -58,7 +58,7 @@ func TestIntJsonTestcases(t *testing.T) {
|
||||
|
||||
// checkNumberTest checks that the output of a number value renderer
|
||||
// matches the expected string. Only use it to test numbers.
|
||||
func checkNumberTest(t *testing.T, r valuerenderer.ValueRenderer, pv protoreflect.Value, expected string) {
|
||||
func checkNumberTest(t *testing.T, r textual.ValueRenderer, pv protoreflect.Value, expected string) {
|
||||
screens, err := r.Format(context.Background(), pv)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, screens, 1)
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -7,10 +7,10 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
bankv1beta1 "cosmossdk.io/api/cosmos/bank/v1beta1"
|
||||
"cosmossdk.io/tx/textual"
|
||||
"cosmossdk.io/tx/textual/internal/testpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
@ -22,21 +22,21 @@ func EmptyCoinMetadataQuerier(ctx context.Context, denom string) (*bankv1beta1.M
|
||||
|
||||
type messageJsonTest struct {
|
||||
Proto *testpb.Foo
|
||||
Screens []valuerenderer.Screen
|
||||
Screens []textual.Screen
|
||||
}
|
||||
|
||||
func TestMessageJsonTestcases(t *testing.T) {
|
||||
raw, err := os.ReadFile("../internal/testdata/message.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/message.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
var testcases []messageJsonTest
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
tr := valuerenderer.NewTextual(EmptyCoinMetadataQuerier)
|
||||
tr := textual.NewTextual(EmptyCoinMetadataQuerier)
|
||||
for i, tc := range testcases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
rend := valuerenderer.NewMessageValueRenderer(&tr, (&testpb.Foo{}).ProtoReflect().Descriptor())
|
||||
rend := textual.NewMessageValueRenderer(&tr, (&testpb.Foo{}).ProtoReflect().Descriptor())
|
||||
|
||||
screens, err := rend.Format(context.Background(), protoreflect.ValueOf(tc.Proto.ProtoReflect()))
|
||||
require.NoError(t, err)
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -7,9 +7,9 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"cosmossdk.io/tx/textual"
|
||||
"cosmossdk.io/tx/textual/internal/testpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
@ -17,7 +17,7 @@ import (
|
||||
|
||||
type repeatedJsonTest struct {
|
||||
Proto *testpb.Qux
|
||||
Screens []valuerenderer.Screen
|
||||
Screens []textual.Screen
|
||||
// TODO Remove once we finished all primitive value renderers parsing
|
||||
// https://github.com/cosmos/cosmos-sdk/pull/13696
|
||||
// https://github.com/cosmos/cosmos-sdk/pull/13853
|
||||
@ -25,20 +25,20 @@ type repeatedJsonTest struct {
|
||||
}
|
||||
|
||||
func TestRepeatedJsonTestcases(t *testing.T) {
|
||||
raw, err := os.ReadFile("../internal/testdata/repeated.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/repeated.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
var testcases []repeatedJsonTest
|
||||
err = json.Unmarshal(raw, &testcases)
|
||||
require.NoError(t, err)
|
||||
|
||||
tr := valuerenderer.NewTextual(mockCoinMetadataQuerier)
|
||||
tr := textual.NewTextual(mockCoinMetadataQuerier)
|
||||
for i, tc := range testcases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
// Create a context.Context containing all coins metadata, to simulate
|
||||
// that they are in state.
|
||||
ctx := context.Background()
|
||||
rend := valuerenderer.NewMessageValueRenderer(&tr, (&testpb.Qux{}).ProtoReflect().Descriptor())
|
||||
rend := textual.NewMessageValueRenderer(&tr, (&testpb.Qux{}).ProtoReflect().Descriptor())
|
||||
require.NoError(t, err)
|
||||
|
||||
screens, err := rend.Format(ctx, protoreflect.ValueOf(tc.Proto.ProtoReflect()))
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -7,7 +7,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
"cosmossdk.io/tx/textual"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
)
|
||||
@ -17,7 +17,7 @@ type stringJsonTest struct {
|
||||
}
|
||||
|
||||
func TestStringJsonTestcases(t *testing.T) {
|
||||
raw, err := os.ReadFile("../internal/testdata/string.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/string.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
var testcases []stringJsonTest
|
||||
@ -26,7 +26,7 @@ func TestStringJsonTestcases(t *testing.T) {
|
||||
|
||||
for i, tc := range testcases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
rend := valuerenderer.NewStringValueRenderer()
|
||||
rend := textual.NewStringValueRenderer()
|
||||
|
||||
screens, err := rend.Format(context.Background(), protoreflect.ValueOfString(tc.Text))
|
||||
require.NoError(t, err)
|
||||
@ -44,7 +44,7 @@ func TestStringHighUnicode(t *testing.T) {
|
||||
// We cannot encode Unicode characters beyond the BMP directly in JSON,
|
||||
// so this case must be a native Go test.
|
||||
s := "\U00101234"
|
||||
rend := valuerenderer.NewStringValueRenderer()
|
||||
rend := textual.NewStringValueRenderer()
|
||||
screens, err := rend.Format(context.Background(), protoreflect.ValueOfString(s))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(screens))
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -8,7 +8,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
"cosmossdk.io/tx/textual"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
@ -32,7 +32,7 @@ type timestampJsonTest struct {
|
||||
}
|
||||
|
||||
func TestTimestampJsonTestcases(t *testing.T) {
|
||||
raw, err := os.ReadFile("../internal/testdata/timestamp.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/timestamp.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
var testcases []timestampJsonTest
|
||||
@ -41,9 +41,9 @@ func TestTimestampJsonTestcases(t *testing.T) {
|
||||
|
||||
for i, tc := range testcases {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
rend := valuerenderer.NewTimestampValueRenderer()
|
||||
rend := textual.NewTimestampValueRenderer()
|
||||
|
||||
var screens []valuerenderer.Screen
|
||||
var screens []textual.Screen
|
||||
if tc.Proto != nil {
|
||||
screens, err = rend.Format(context.Background(), protoreflect.ValueOf(tc.Proto.ProtoReflect()))
|
||||
if tc.Error {
|
||||
@ -70,7 +70,7 @@ func TestTimestampJsonTestcases(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTimestampBadFormat(t *testing.T) {
|
||||
rend := valuerenderer.NewTimestampValueRenderer()
|
||||
rend := textual.NewTimestampValueRenderer()
|
||||
_, err := rend.Format(context.Background(), protoreflect.ValueOf(dur.New(time.Hour).ProtoReflect()))
|
||||
require.Error(t, err)
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -24,8 +24,8 @@ import (
|
||||
_ "cosmossdk.io/api/cosmos/gov/v1"
|
||||
txv1beta1 "cosmossdk.io/api/cosmos/tx/v1beta1"
|
||||
"cosmossdk.io/tx/signing"
|
||||
"cosmossdk.io/tx/textual"
|
||||
"cosmossdk.io/tx/textual/internal/textualpb"
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
)
|
||||
|
||||
// txJsonTestTx represents the type that in the JSON test
|
||||
@ -43,11 +43,11 @@ type txJsonTest struct {
|
||||
SignerData json.RawMessage `json:"signer_data"`
|
||||
Metadata *bankv1beta1.Metadata
|
||||
Error bool
|
||||
Screens []valuerenderer.Screen
|
||||
Screens []textual.Screen
|
||||
}
|
||||
|
||||
func TestTxJsonTestcases(t *testing.T) {
|
||||
raw, err := os.ReadFile("../internal/testdata/tx.json")
|
||||
raw, err := os.ReadFile("./internal/testdata/tx.json")
|
||||
require.NoError(t, err)
|
||||
|
||||
var testcases []txJsonTest
|
||||
@ -58,8 +58,8 @@ func TestTxJsonTestcases(t *testing.T) {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
txBody, bodyBz, txAuthInfo, authInfoBz, signerData := createTextualData(t, tc.Proto, tc.SignerData)
|
||||
|
||||
tr := valuerenderer.NewTextual(mockCoinMetadataQuerier)
|
||||
rend := valuerenderer.NewTxValueRenderer(&tr)
|
||||
tr := textual.NewTextual(mockCoinMetadataQuerier)
|
||||
rend := textual.NewTxValueRenderer(&tr)
|
||||
ctx := addMetadataToContext(context.Background(), tc.Metadata)
|
||||
|
||||
data := &textualpb.TextualData{
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer
|
||||
package textual
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -22,7 +22,7 @@ import (
|
||||
// metadata. It is meant to be passed as an argument into `NewTextual`.
|
||||
type CoinMetadataQueryFn func(ctx context.Context, denom string) (*bankv1beta1.Metadata, error)
|
||||
|
||||
// ValueRendererCreator is a function returning a ValueRenderer.
|
||||
// ValueRendererCreator is a function returning a textual.
|
||||
type ValueRendererCreator func(protoreflect.FieldDescriptor) ValueRenderer
|
||||
|
||||
// Textual holds the configuration for dispatching
|
||||
@ -115,6 +115,10 @@ func (r *Textual) GetMessageValueRenderer(md protoreflect.MessageDescriptor) (Va
|
||||
return NewMessageValueRenderer(r, md), nil
|
||||
}
|
||||
|
||||
// init initializes Textual's internal `scalars` and `messages` registry for
|
||||
// custom scalar and message renderers.
|
||||
//
|
||||
// It is an idempotent method.
|
||||
func (r *Textual) init() {
|
||||
if r.scalars == nil {
|
||||
r.scalars = map[string]ValueRendererCreator{}
|
||||
@ -137,6 +141,12 @@ func (r *Textual) DefineScalar(scalar string, vr ValueRendererCreator) {
|
||||
r.scalars[scalar] = vr
|
||||
}
|
||||
|
||||
// DefineMessageRenderer adds a new custom message renderer.
|
||||
func (r *Textual) DefineMessageRenderer(name protoreflect.FullName, vr ValueRenderer) {
|
||||
r.init()
|
||||
r.messages[name] = vr
|
||||
}
|
||||
|
||||
// GetSignBytes returns the transaction sign bytes.
|
||||
func (r *Textual) GetSignBytes(ctx context.Context, bodyBz, authInfoBz []byte, signerData signing.SignerData) ([]byte, error) {
|
||||
data := &textualpb.TextualData{
|
||||
@ -1,4 +1,4 @@
|
||||
package valuerenderer_test
|
||||
package textual_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@ -7,34 +7,34 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"cosmossdk.io/tx/textual"
|
||||
"cosmossdk.io/tx/textual/internal/testpb"
|
||||
"cosmossdk.io/tx/textual/valuerenderer"
|
||||
)
|
||||
|
||||
func TestDispatcher(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
expErr bool
|
||||
expValueRenderer valuerenderer.ValueRenderer
|
||||
expValueRenderer textual.ValueRenderer
|
||||
}{
|
||||
{"UINT32", false, valuerenderer.NewIntValueRenderer(fieldDescriptorFromName("UINT32"))},
|
||||
{"UINT64", false, valuerenderer.NewIntValueRenderer(fieldDescriptorFromName("UINT64"))},
|
||||
{"SDKINT", false, valuerenderer.NewIntValueRenderer(fieldDescriptorFromName("SDKINT"))},
|
||||
{"SDKDEC", false, valuerenderer.NewDecValueRenderer()},
|
||||
{"BYTES", false, valuerenderer.NewBytesValueRenderer()},
|
||||
{"TIMESTAMP", false, valuerenderer.NewTimestampValueRenderer()},
|
||||
{"DURATION", false, valuerenderer.NewDurationValueRenderer()},
|
||||
{"COIN", false, valuerenderer.NewCoinsValueRenderer(nil)},
|
||||
{"COINS", false, valuerenderer.NewCoinsValueRenderer(nil)},
|
||||
{"ENUM", false, valuerenderer.NewEnumValueRenderer(fieldDescriptorFromName("ENUM"))},
|
||||
{"ANY", false, valuerenderer.NewAnyValueRenderer(nil)},
|
||||
{"UINT32", false, textual.NewIntValueRenderer(fieldDescriptorFromName("UINT32"))},
|
||||
{"UINT64", false, textual.NewIntValueRenderer(fieldDescriptorFromName("UINT64"))},
|
||||
{"SDKINT", false, textual.NewIntValueRenderer(fieldDescriptorFromName("SDKINT"))},
|
||||
{"SDKDEC", false, textual.NewDecValueRenderer()},
|
||||
{"BYTES", false, textual.NewBytesValueRenderer()},
|
||||
{"TIMESTAMP", false, textual.NewTimestampValueRenderer()},
|
||||
{"DURATION", false, textual.NewDurationValueRenderer()},
|
||||
{"COIN", false, textual.NewCoinsValueRenderer(nil)},
|
||||
{"COINS", false, textual.NewCoinsValueRenderer(nil)},
|
||||
{"ENUM", false, textual.NewEnumValueRenderer(fieldDescriptorFromName("ENUM"))},
|
||||
{"ANY", false, textual.NewAnyValueRenderer(nil)},
|
||||
{"FLOAT", true, nil},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
textual := valuerenderer.NewTextual(nil)
|
||||
textual := textual.NewTextual(nil)
|
||||
rend, err := textual.GetFieldValueRenderer(fieldDescriptorFromName(tc.name))
|
||||
|
||||
if tc.expErr {
|
||||
Loading…
Reference in New Issue
Block a user