diff --git a/internal/msgs/en_error_messages.go b/internal/msgs/en_error_messages.go index 1b99587..7d047c6 100644 --- a/internal/msgs/en_error_messages.go +++ b/internal/msgs/en_error_messages.go @@ -83,11 +83,12 @@ var ( MsgFailedToBuildExistingConfirmationInvalid = ffe("FF23063", "Failed to build confirmations, existing confirmations are not valid") MsgFromBlockInvalid = ffe("FF23064", "From block invalid. Must be 'earliest', 'latest' or a decimal: %s", http.StatusBadRequest) MsgInvalidJSONFormatOptions = ffe("FF23065", "The JSON formatting options must be a valid set of key=value pairs in URL query string format '%s'") - MsgUnknownJSONFormatOptions = ffe("FF23066", "JSON formatting option unknown %s=%s") + MsgUnknownJSONFormatOption = ffe("FF23066", "Unknown JSON formatting option '%s=%s'. Options must be URL query string key=value pairs (e.g. mode=object&number=hex-0x). Supported options and values: %s") MsgObservedPanic = ffe("FF23067", "Observed panic: %v") MsgReturnedBlockHashMismatch = ffe("FF23068", "Returned block %d hash %s does not match requested hash %s") MsgInvalidChainTrackingMode = ffe("FF23069", "Invalid chain tracking mode '%s': must be 'light' or 'full'") MsgTransactionNotIncludedInChainHead = ffe("FF23070", "Transaction '%s' cannot be reconciled because chain head %d is before receipt block %s") MsgTransactionEstimateTooLargeForBlock = ffe("FF23071", "Gas estimate %s (scaled at %.2f from estimate %s) too large for the current block gas limit %s") MsgMonitoredHeadLengthInvalid = ffe("FF23072", "Monitored head length must be greater than or equal to 1 value=%d") + MsgUnknownJSONFormatOptionValue = ffe("FF23073", "Unknown value '%s' for JSON formatting option '%s'. Supported values: %s") ) diff --git a/pkg/ethrpc/jsonformatoptions.go b/pkg/ethrpc/jsonformatoptions.go index 7f5a393..b44235d 100644 --- a/pkg/ethrpc/jsonformatoptions.go +++ b/pkg/ethrpc/jsonformatoptions.go @@ -19,6 +19,7 @@ package ethrpc import ( "context" "encoding/json" + "fmt" "math/big" "net/url" "strings" @@ -53,6 +54,32 @@ const optChecksum = "checksum" const optSelfDescribing = "self-describing" const optPretty = "pretty" +var jsonFormatOptionSupportedValues = map[string][]string{ + "mode": {optObject, optArray, optSelfDescribing}, + "number": {optString, optHex0x, optHex, optJSONNumber}, + "bytes": {optHex0x, optHex, optHexPlain, optBase64}, + "address": {optHex0x, optHex, optHexPlain, optChecksum}, + optPretty: {"true", "false"}, +} + +func unknownJSONFormatOptionNameError(ctx context.Context, option, value string) error { + + var jsonFormatOptionNames []string + for name := range jsonFormatOptionSupportedValues { + jsonFormatOptionNames = append(jsonFormatOptionNames, name) + } + parts := make([]string, len(jsonFormatOptionNames)) + for i, name := range jsonFormatOptionNames { + parts[i] = fmt.Sprintf("%s (%s)", name, strings.Join(jsonFormatOptionSupportedValues[name], ", ")) + } + return i18n.NewError(ctx, msgs.MsgUnknownJSONFormatOption, option, value, strings.Join(parts, "; ")) +} + +func unknownJSONFormatOptionValueError(ctx context.Context, option, value string) error { + values := jsonFormatOptionSupportedValues[strings.ToLower(option)] + return i18n.NewError(ctx, msgs.MsgUnknownJSONFormatOptionValue, value, option, strings.Join(values, ", ")) +} + func (jfo JSONFormatOptions) GetABISerializer(ctx context.Context) (serializer *abi.Serializer, err error) { return jfo.getABISerializer(ctx, false) } @@ -94,7 +121,7 @@ func (jfo JSONFormatOptions) GetSerializerSet(ctx context.Context, skipErrors bo ss.Mode = abi.FormatAsSelfDescribingArrays default: if !skipErrors { - return nil, i18n.WrapError(ctx, err, msgs.MsgUnknownJSONFormatOptions, option, v) + return nil, unknownJSONFormatOptionValueError(ctx, option, v) } } case "number": @@ -107,7 +134,7 @@ func (jfo JSONFormatOptions) GetSerializerSet(ctx context.Context, skipErrors bo ss.Integer = abi.JSONNumberIntSerializer default: if !skipErrors { - return nil, i18n.WrapError(ctx, err, msgs.MsgUnknownJSONFormatOptions, option, v) + return nil, unknownJSONFormatOptionValueError(ctx, option, v) } } case "bytes": @@ -119,7 +146,9 @@ func (jfo JSONFormatOptions) GetSerializerSet(ctx context.Context, skipErrors bo case optBase64: ss.Bytes = abi.Base64ByteSerializer default: - return nil, i18n.WrapError(ctx, err, msgs.MsgUnknownJSONFormatOptions, option, v) + if !skipErrors { + return nil, unknownJSONFormatOptionValueError(ctx, option, v) + } } case "address": switch strings.ToLower(v) { @@ -130,13 +159,15 @@ func (jfo JSONFormatOptions) GetSerializerSet(ctx context.Context, skipErrors bo case optChecksum: ss.Address = abi.ChecksumAddrSerializer default: - return nil, i18n.WrapError(ctx, err, msgs.MsgUnknownJSONFormatOptions, option, v) + if !skipErrors { + return nil, unknownJSONFormatOptionValueError(ctx, option, v) + } } case optPretty: ss.Pretty = (v != "false") default: if !skipErrors { - return nil, i18n.WrapError(ctx, err, msgs.MsgUnknownJSONFormatOptions, option, v) + return nil, unknownJSONFormatOptionNameError(ctx, option, v) } } } diff --git a/pkg/ethrpc/jsonformatoptions_test.go b/pkg/ethrpc/jsonformatoptions_test.go index 3e0bddd..d724e88 100644 --- a/pkg/ethrpc/jsonformatoptions_test.go +++ b/pkg/ethrpc/jsonformatoptions_test.go @@ -239,18 +239,29 @@ func TestJSONFormatOptionErrors(t *testing.T) { _, err = JSONFormatOptions("color=blue").GetABISerializer(ctx) assert.Regexp(t, "FF23066", err) + assert.Contains(t, err.Error(), "color=blue") + assert.Contains(t, err.Error(), "Supported options and values:") + assert.Contains(t, err.Error(), "mode (object, array, self-describing)") _, err = JSONFormatOptions("mode=blue").GetABISerializer(ctx) - assert.Regexp(t, "FF23066", err) + assert.Regexp(t, "FF23073", err) + assert.Contains(t, err.Error(), "mode") + assert.Contains(t, err.Error(), "Supported values: object, array, self-describing") _, err = JSONFormatOptions("number=blue").GetABISerializer(ctx) - assert.Regexp(t, "FF23066", err) + assert.Regexp(t, "FF23073", err) + assert.Contains(t, err.Error(), "number") + assert.Contains(t, err.Error(), "Supported values: string, hex-0x, hex, json-number") _, err = JSONFormatOptions("bytes=blue").GetABISerializer(ctx) - assert.Regexp(t, "FF23066", err) + assert.Regexp(t, "FF23073", err) + assert.Contains(t, err.Error(), "bytes") + assert.Contains(t, err.Error(), "Supported values: hex-0x, hex, hex-plain, base64") _, err = JSONFormatOptions("address=blue").GetABISerializer(ctx) - assert.Regexp(t, "FF23066", err) + assert.Regexp(t, "FF23073", err) + assert.Contains(t, err.Error(), "address") + assert.Contains(t, err.Error(), "Supported values: hex-0x, hex, hex-plain, checksum") s := JSONFormatOptions("this;is;ignored").GetABISerializerIgnoreErrors(ctx) assert.NotNil(t, s)