Skip to content

Commit

Permalink
Merge pull request Agoric#45 from cosmos/sunny/output-structs-in-querier
Browse files Browse the repository at this point in the history
R4R: Json and Stringer structs in querier instead of CLI
  • Loading branch information
sunnya97 authored Mar 7, 2019
2 parents 1313144 + 11e8ee1 commit 9be9007
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 61 deletions.
36 changes: 5 additions & 31 deletions tutorial/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ package cli

import (
"fmt"
"strings"

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
Expand All @@ -39,19 +38,13 @@ func GetCmdResolveName(queryRoute string, cdc *codec.Codec) *cobra.Command {
return nil
}

return cliCtx.PrintOutput(resolveRes{string(res)})
var out nameservice.QueryResResolve
cdc.MustUnmarshalJSON(res, &out)
return cliCtx.PrintOutput(out)
},
}
}

type resolveRes struct {
Value string `json:"value"`
}

func (r resolveRes) String() string {
return r.Value
}

// GetCmdWhois queries information about a domain
func GetCmdWhois(queryRoute string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Expand All @@ -68,22 +61,13 @@ func GetCmdWhois(queryRoute string, cdc *codec.Codec) *cobra.Command {
return nil
}

var out whoIsRes
var out nameservice.Whois
cdc.MustUnmarshalJSON(res, &out)
return cliCtx.PrintOutput(out)
},
}
}

type whoIsRes nameservice.Whois

func (w whoIsRes) String() string {
return strings.TrimSpace(fmt.Sprintf(`Owner: %s
Value: %s
Price: %s`, w.Owner, w.Value, w.Price))
}


// GetCmdNames queries a list of all names
func GetCmdNames(queryRoute string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Expand All @@ -99,18 +83,12 @@ func GetCmdNames(queryRoute string, cdc *codec.Codec) *cobra.Command {
return nil
}

var out namesList
var out nameservice.QueryResNames
cdc.MustUnmarshalJSON(res, &out)
return cliCtx.PrintOutput(out)
},
}
}

type namesList []string

func (n namesList) String() string {
return strings.Join(n[:], "\n")
}
```

Notes on the above code:
Expand All @@ -121,10 +99,6 @@ Notes on the above code:
- The second piece (`nameservice`) is the name of the module to route the query to.
- Finally there is the specific querier in the module that will be called.
- In this example the fourth piece is the query. This works because the query parameter is a simple string. To enable more complex query inputs you need to use the second argument of the [`.QueryWithData()`](https://godoc.org/github.com/cosmos/cosmos-sdk/client/context#CLIContext.QueryWithData) function to pass in `data`. For an example of this see the [queriers in the Staking module](https://github.com/cosmos/cosmos-sdk/blob/develop/x/stake/querier/querier.go#L103).
- Each output type should be something that is both JSON marshallable and stringable (implements the Golang `fmt.Stringer` interface).
- So for the output type of `resolve` we wrap the resolution string in a struct called `resolveRes` which is both JSON marshallable and has a `.String()` method.
- For the output of Whois, the normal Whois struct is already JSON marshalable, but we need to add a `.String()` method on it.
- Same for the output of a names query, a `[]string` is already natively marshalable, but we want to add a `.String()` method on it.

## Transactions

Expand Down
44 changes: 42 additions & 2 deletions tutorial/queriers.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ Start by defining the `NewQuerier` function which acts as a sub-router for queri
package nameservice

import (
"fmt"
"strings"

"github.com/cosmos/cosmos-sdk/codec"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -21,6 +24,7 @@ import (
const (
QueryResolve = "resolve"
QueryWhois = "whois"
QueryNames = "names"
)

// NewQuerier is the module level router for state queries
Expand All @@ -31,6 +35,8 @@ func NewQuerier(keeper Keeper) sdk.Querier {
return queryResolve(ctx, path[1:], req, keeper)
case QueryWhois:
return queryWhois(ctx, path[1:], req, keeper)
case QueryNames:
return queryNames(ctx, req, keeper)
default:
return nil, sdk.ErrUnknownRequest("unknown nameservice query endpoint")
}
Expand All @@ -51,7 +57,22 @@ func queryResolve(ctx sdk.Context, path []string, req abci.RequestQuery, keeper
return []byte{}, sdk.ErrUnknownRequest("could not resolve name")
}

return []byte(value), nil
bz, err2 := codec.MarshalJSONIndent(keeper.cdc, QueryResResolve{value})
if err2 != nil {
panic("could not marshal result to JSON")
}

return bz, nil
}

// Query Result Payload for a resolve query
type QueryResResolve struct {
Value string `json:"value"`
}

// implement fmt.Stringer
func (r QueryResResolve) String() string {
return r.Value
}

// nolint: unparam
Expand All @@ -68,8 +89,15 @@ func queryWhois(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke
return bz, nil
}

// implement fmt.Stringer
func (w Whois) String() string {
return strings.TrimSpace(fmt.Sprintf(`Owner: %s
Value: %s
Price: %s`, w.Owner, w.Value, w.Price))
}

func queryNames(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
var namesList []string
var namesList QueryResNames

iterator := keeper.GetNamesIterator(ctx)

Expand All @@ -85,10 +113,22 @@ func queryNames(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []by

return bz, nil
}

// Query Result Payload for a names query
type QueryResNames []string

// implement fmt.Stringer
func (n QueryResNames) String() string {
return strings.Join(n[:], "\n")
}
```

Notes on the above code:

- Here your `Keeper`'s getters and setters come into heavy use. When building any other applications that use this module you may need to go back and define more getters/setters to access the pieces of state you need.
- By convention, each output type should be something that is both JSON marshallable and stringable (implements the Golang `fmt.Stringer` interface). The returned bytes should be the JSON encoding of the output result.
- So for the output type of `resolve` we wrap the resolution string in a struct called `QueryResResolve` which is both JSON marshallable and has a `.String()` method.
- For the output of Whois, the normal Whois struct is already JSON marshalable, but we need to add a `.String()` method on it.
- Same for the output of a names query, a `[]string` is already natively marshalable, but we want to add a `.String()` method on it.

### Now that you have ways to mutate and view your module state it's time to put the finishing touches on it! Register your types in the [Amino encoding format next](./codec.md)!
31 changes: 5 additions & 26 deletions x/nameservice/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cli

import (
"fmt"
"strings"

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
Expand All @@ -26,19 +25,13 @@ func GetCmdResolveName(queryRoute string, cdc *codec.Codec) *cobra.Command {
return nil
}

return cliCtx.PrintOutput(resolveRes{string(res)})
var out nameservice.QueryResResolve
cdc.MustUnmarshalJSON(res, &out)
return cliCtx.PrintOutput(out)
},
}
}

type resolveRes struct {
Value string `json:"value"`
}

func (r resolveRes) String() string {
return r.Value
}

// GetCmdWhois queries information about a domain
func GetCmdWhois(queryRoute string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Expand All @@ -55,21 +48,13 @@ func GetCmdWhois(queryRoute string, cdc *codec.Codec) *cobra.Command {
return nil
}

var out whoIsRes
var out nameservice.Whois
cdc.MustUnmarshalJSON(res, &out)
return cliCtx.PrintOutput(out)
},
}
}

type whoIsRes nameservice.Whois

func (w whoIsRes) String() string {
return strings.TrimSpace(fmt.Sprintf(`Owner: %s
Value: %s
Price: %s`, w.Owner, w.Value, w.Price))
}

// GetCmdNames queries a list of all names
func GetCmdNames(queryRoute string, cdc *codec.Codec) *cobra.Command {
return &cobra.Command{
Expand All @@ -85,15 +70,9 @@ func GetCmdNames(queryRoute string, cdc *codec.Codec) *cobra.Command {
return nil
}

var out namesList
var out nameservice.QueryResNames
cdc.MustUnmarshalJSON(res, &out)
return cliCtx.PrintOutput(out)
},
}
}

type namesList []string

func (n namesList) String() string {
return strings.Join(n[:], "\n")
}
37 changes: 35 additions & 2 deletions x/nameservice/querier.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package nameservice

import (
"fmt"
"strings"

"github.com/cosmos/cosmos-sdk/codec"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -40,7 +43,22 @@ func queryResolve(ctx sdk.Context, path []string, req abci.RequestQuery, keeper
return []byte{}, sdk.ErrUnknownRequest("could not resolve name")
}

return []byte(value), nil
bz, err2 := codec.MarshalJSONIndent(keeper.cdc, QueryResResolve{value})
if err2 != nil {
panic("could not marshal result to JSON")
}

return bz, nil
}

// Query Result Payload for a resolve query
type QueryResResolve struct {
Value string `json:"value"`
}

// implement fmt.Stringer
func (r QueryResResolve) String() string {
return r.Value
}

// nolint: unparam
Expand All @@ -57,8 +75,15 @@ func queryWhois(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke
return bz, nil
}

// implement fmt.Stringer
func (w Whois) String() string {
return strings.TrimSpace(fmt.Sprintf(`Owner: %s
Value: %s
Price: %s`, w.Owner, w.Value, w.Price))
}

func queryNames(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) {
var namesList []string
var namesList QueryResNames

iterator := keeper.GetNamesIterator(ctx)

Expand All @@ -74,3 +99,11 @@ func queryNames(ctx sdk.Context, req abci.RequestQuery, keeper Keeper) (res []by

return bz, nil
}

// Query Result Payload for a names query
type QueryResNames []string

// implement fmt.Stringer
func (n QueryResNames) String() string {
return strings.Join(n[:], "\n")
}

0 comments on commit 9be9007

Please sign in to comment.