Merge PR #3688: JSON Decode Log in REST Client
This commit is contained in:
parent
2121160d29
commit
0611d2eda2
|
@ -39,6 +39,9 @@
|
||||||
|
|
||||||
### Gaia REST API
|
### Gaia REST API
|
||||||
|
|
||||||
|
* Update the `TxResponse` type allowing for the `Logs` result to be JSON
|
||||||
|
decoded automatically.
|
||||||
|
|
||||||
### Gaia CLI
|
### Gaia CLI
|
||||||
|
|
||||||
### Gaia
|
### Gaia
|
||||||
|
|
|
@ -629,15 +629,9 @@ func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) (ctx sdk.Con
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type indexedABCILog struct {
|
|
||||||
MsgIndex int `json:"msg_index"`
|
|
||||||
Success bool `json:"success"`
|
|
||||||
Log string `json:"log"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// runMsgs iterates through all the messages and executes them.
|
// runMsgs iterates through all the messages and executes them.
|
||||||
func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (result sdk.Result) {
|
func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (result sdk.Result) {
|
||||||
idxlogs := make([]indexedABCILog, 0, len(msgs)) // a list of JSON-encoded logs with msg index
|
idxlogs := make([]sdk.ABCIMessageLog, 0, len(msgs)) // a list of JSON-encoded logs with msg index
|
||||||
|
|
||||||
var data []byte // NOTE: we just append them all (?!)
|
var data []byte // NOTE: we just append them all (?!)
|
||||||
var tags sdk.Tags // also just append them all
|
var tags sdk.Tags // also just append them all
|
||||||
|
@ -666,7 +660,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re
|
||||||
tags = append(tags, sdk.MakeTag(sdk.TagAction, msg.Type()))
|
tags = append(tags, sdk.MakeTag(sdk.TagAction, msg.Type()))
|
||||||
tags = append(tags, msgResult.Tags...)
|
tags = append(tags, msgResult.Tags...)
|
||||||
|
|
||||||
idxLog := indexedABCILog{MsgIndex: msgIdx, Log: msgResult.Log}
|
idxLog := sdk.ABCIMessageLog{MsgIndex: msgIdx, Log: msgResult.Log}
|
||||||
|
|
||||||
// stop execution and return on first failed message
|
// stop execution and return on first failed message
|
||||||
if !msgResult.IsOK() {
|
if !msgResult.IsOK() {
|
||||||
|
|
|
@ -254,6 +254,7 @@ func (ctx CLIContext) PrintOutput(toPrint fmt.Stringer) (err error) {
|
||||||
switch ctx.OutputFormat {
|
switch ctx.OutputFormat {
|
||||||
case "text":
|
case "text":
|
||||||
out = []byte(toPrint.String())
|
out = []byte(toPrint.String())
|
||||||
|
|
||||||
case "json":
|
case "json":
|
||||||
if ctx.Indent {
|
if ctx.Indent {
|
||||||
out, err = ctx.Codec.MarshalJSONIndent(toPrint, "", " ")
|
out, err = ctx.Codec.MarshalJSONIndent(toPrint, "", " ")
|
||||||
|
|
|
@ -192,6 +192,9 @@ func PostProcessResponse(w http.ResponseWriter, cdc *codec.Codec, response inter
|
||||||
var output []byte
|
var output []byte
|
||||||
|
|
||||||
switch response.(type) {
|
switch response.(type) {
|
||||||
|
case []byte:
|
||||||
|
output = response.([]byte)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
var err error
|
var err error
|
||||||
if indent {
|
if indent {
|
||||||
|
@ -203,8 +206,6 @@ func PostProcessResponse(w http.ResponseWriter, cdc *codec.Codec, response inter
|
||||||
WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case []byte:
|
|
||||||
output = response.([]byte)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -9,7 +10,6 @@ import (
|
||||||
|
|
||||||
// Result is the union of ResponseFormat and ResponseCheckTx.
|
// Result is the union of ResponseFormat and ResponseCheckTx.
|
||||||
type Result struct {
|
type Result struct {
|
||||||
|
|
||||||
// Code is the response code, is stored back on the chain.
|
// Code is the response code, is stored back on the chain.
|
||||||
Code CodeType
|
Code CodeType
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ type Result struct {
|
||||||
// results from multiple msgs executions
|
// results from multiple msgs executions
|
||||||
Data []byte
|
Data []byte
|
||||||
|
|
||||||
// Log is just debug information. NOTE: nondeterministic.
|
// Log contains the txs log information. NOTE: nondeterministic.
|
||||||
Log string
|
Log string
|
||||||
|
|
||||||
// GasWanted is the maximum units of work we allow this tx to perform.
|
// GasWanted is the maximum units of work we allow this tx to perform.
|
||||||
|
@ -39,19 +39,42 @@ func (res Result) IsOK() bool {
|
||||||
return res.Code.IsOK()
|
return res.Code.IsOK()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is a version of TxResponse where the tags are StringTags rather than []byte tags
|
// ABCIMessageLogs represents a slice of ABCIMessageLog.
|
||||||
|
type ABCIMessageLogs []ABCIMessageLog
|
||||||
|
|
||||||
|
// ABCIMessageLog defines a structure containing an indexed tx ABCI message log.
|
||||||
|
type ABCIMessageLog struct {
|
||||||
|
MsgIndex int `json:"msg_index"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Log string `json:"log"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements the fmt.Stringer interface for the ABCIMessageLogs type.
|
||||||
|
func (logs ABCIMessageLogs) String() (str string) {
|
||||||
|
if logs != nil {
|
||||||
|
raw, err := json.Marshal(logs)
|
||||||
|
if err == nil {
|
||||||
|
str = string(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxResponse defines a structure containing relevant tx data and metadata. The
|
||||||
|
// tags are stringified and the log is JSON decoded.
|
||||||
type TxResponse struct {
|
type TxResponse struct {
|
||||||
Height int64 `json:"height"`
|
Height int64 `json:"height"`
|
||||||
TxHash string `json:"txhash"`
|
TxHash string `json:"txhash"`
|
||||||
Code uint32 `json:"code,omitempty"`
|
Code uint32 `json:"code,omitempty"`
|
||||||
Data []byte `json:"data,omitempty"`
|
Data []byte `json:"data,omitempty"`
|
||||||
Log string `json:"log,omitempty"`
|
Logs ABCIMessageLogs `json:"logs,omitempty"`
|
||||||
Info string `json:"info,omitempty"`
|
Info string `json:"info,omitempty"`
|
||||||
GasWanted int64 `json:"gas_wanted,omitempty"`
|
GasWanted int64 `json:"gas_wanted,omitempty"`
|
||||||
GasUsed int64 `json:"gas_used,omitempty"`
|
GasUsed int64 `json:"gas_used,omitempty"`
|
||||||
Tags StringTags `json:"tags,omitempty"`
|
Tags StringTags `json:"tags,omitempty"`
|
||||||
Codespace string `json:"codespace,omitempty"`
|
Codespace string `json:"codespace,omitempty"`
|
||||||
Tx Tx `json:"tx,omitempty"`
|
Tx Tx `json:"tx,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewResponseResultTx returns a TxResponse given a ResultTx from tendermint
|
// NewResponseResultTx returns a TxResponse given a ResultTx from tendermint
|
||||||
|
@ -60,12 +83,14 @@ func NewResponseResultTx(res *ctypes.ResultTx, tx Tx) TxResponse {
|
||||||
return TxResponse{}
|
return TxResponse{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parsedLogs, _ := ParseABCILogs(res.TxResult.Log)
|
||||||
|
|
||||||
return TxResponse{
|
return TxResponse{
|
||||||
TxHash: res.Hash.String(),
|
TxHash: res.Hash.String(),
|
||||||
Height: res.Height,
|
Height: res.Height,
|
||||||
Code: res.TxResult.Code,
|
Code: res.TxResult.Code,
|
||||||
Data: res.TxResult.Data,
|
Data: res.TxResult.Data,
|
||||||
Log: res.TxResult.Log,
|
Logs: parsedLogs,
|
||||||
Info: res.TxResult.Info,
|
Info: res.TxResult.Info,
|
||||||
GasWanted: res.TxResult.GasWanted,
|
GasWanted: res.TxResult.GasWanted,
|
||||||
GasUsed: res.TxResult.GasUsed,
|
GasUsed: res.TxResult.GasUsed,
|
||||||
|
@ -85,12 +110,14 @@ func NewResponseFormatBroadcastTxCommit(res *ctypes.ResultBroadcastTxCommit) TxR
|
||||||
txHash = res.Hash.String()
|
txHash = res.Hash.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parsedLogs, _ := ParseABCILogs(res.DeliverTx.Log)
|
||||||
|
|
||||||
return TxResponse{
|
return TxResponse{
|
||||||
Height: res.Height,
|
Height: res.Height,
|
||||||
TxHash: txHash,
|
TxHash: txHash,
|
||||||
Code: res.DeliverTx.Code,
|
Code: res.DeliverTx.Code,
|
||||||
Data: res.DeliverTx.Data,
|
Data: res.DeliverTx.Data,
|
||||||
Log: res.DeliverTx.Log,
|
Logs: parsedLogs,
|
||||||
Info: res.DeliverTx.Info,
|
Info: res.DeliverTx.Info,
|
||||||
GasWanted: res.DeliverTx.GasWanted,
|
GasWanted: res.DeliverTx.GasWanted,
|
||||||
GasUsed: res.DeliverTx.GasUsed,
|
GasUsed: res.DeliverTx.GasUsed,
|
||||||
|
@ -106,10 +133,12 @@ func NewResponseFormatBroadcastTx(res *ctypes.ResultBroadcastTx) TxResponse {
|
||||||
return TxResponse{}
|
return TxResponse{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parsedLogs, _ := ParseABCILogs(res.Log)
|
||||||
|
|
||||||
return TxResponse{
|
return TxResponse{
|
||||||
Code: res.Code,
|
Code: res.Code,
|
||||||
Data: res.Data.Bytes(),
|
Data: res.Data.Bytes(),
|
||||||
Log: res.Log,
|
Logs: parsedLogs,
|
||||||
TxHash: res.Hash.String(),
|
TxHash: res.Hash.String(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,8 +163,8 @@ func (r TxResponse) String() string {
|
||||||
sb.WriteString(fmt.Sprintf(" Data: %s\n", string(r.Data)))
|
sb.WriteString(fmt.Sprintf(" Data: %s\n", string(r.Data)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Log != "" {
|
if r.Logs != nil {
|
||||||
sb.WriteString(fmt.Sprintf(" Log: %s\n", r.Log))
|
sb.WriteString(fmt.Sprintf(" Logs: %s\n", r.Logs))
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Info != "" {
|
if r.Info != "" {
|
||||||
|
@ -163,5 +192,12 @@ func (r TxResponse) String() string {
|
||||||
|
|
||||||
// Empty returns true if the response is empty
|
// Empty returns true if the response is empty
|
||||||
func (r TxResponse) Empty() bool {
|
func (r TxResponse) Empty() bool {
|
||||||
return r.TxHash == "" && r.Log == ""
|
return r.TxHash == "" && r.Logs == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseABCILogs attempts to parse a stringified ABCI tx log into a slice of
|
||||||
|
// ABCIMessageLog types. It returns an error upon JSON decoding failure.
|
||||||
|
func ParseABCILogs(logs string) (res ABCIMessageLogs, err error) {
|
||||||
|
err = json.Unmarshal([]byte(logs), &res)
|
||||||
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,3 +16,14 @@ func TestResult(t *testing.T) {
|
||||||
res.Code = CodeType(1)
|
res.Code = CodeType(1)
|
||||||
require.False(t, res.IsOK())
|
require.False(t, res.IsOK())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseABCILog(t *testing.T) {
|
||||||
|
logs := `[{"log":"","msg_index":1,"success":true}]`
|
||||||
|
|
||||||
|
res, err := ParseABCILogs(logs)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, res, 1)
|
||||||
|
require.Equal(t, res[0].Log, "")
|
||||||
|
require.Equal(t, res[0].MsgIndex, 1)
|
||||||
|
require.True(t, res[0].Success)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue