Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(templates): use collections for map and remove multi index support #4094

Merged
merged 22 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Features

- [#3707](https://github.com/ignite/cli/pull/3707) Add collections support.
- [#3707](https://github.com/ignite/cli/pull/3707) and [#4094](https://github.com/ignite/cli/pull/4094) Add collections support.
- [#3977](https://github.com/ignite/cli/pull/3977) Add `chain lint` command to lint the chain's codebase using `golangci-lint`
- [#3770](https://github.com/ignite/cli/pull/3770) Add `scaffold configs` and `scaffold params` commands
- [#4001](https://github.com/ignite/cli/pull/4001) Improve `xgenny` dry run
Expand All @@ -19,6 +19,7 @@

### Changes

- [#4094](https://github.com/ignite/cli/pull/4094) Scaffolding a multi-index map using `ignite s map foo bar baz --index foobar,foobaz` is no longer supported. Use one index instead of use `collections.IndexedMap`.
- [#4058](https://github.com/ignite/cli/pull/4058) Simplify scaffolded modules by including `ValidateBasic()` logic in message handler.
- [#4058](https://github.com/ignite/cli/pull/4058) Use `address.Codec` instead of `AccAddressFromBech32`.
- [#3993](https://github.com/ignite/cli/pull/3993) Oracle scaffolding was deprecated and has been removed
Expand Down
19 changes: 5 additions & 14 deletions ignite/cmd/scaffold_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import (
"github.com/ignite/cli/v29/ignite/services/scaffolder"
)

const (
FlagIndexes = "index"
)
const FlagIndexName = "index"

// NewScaffoldMap returns a new command to scaffold a map.
func NewScaffoldMap() *cobra.Command {
Expand Down Expand Up @@ -42,14 +40,7 @@ for the "hello" key.

blogd q blog show-post hello

To customize the index, use the "--index" flag. Multiple indices can be
provided, which simplifies querying values. For example:

ignite scaffold map product price desc --index category,guid

With this command, you would get a "Product" value indexed by both a category
and a GUID (globally unique ID). This will let you programmatically fetch
product values that have the same category but are using different GUIDs.
By default, the index is called "index", to customize the index, use the "--index" flag.

Since the behavior of "list" and "map" scaffolding is very similar, you can use
the "--no-message", "--module", "--signer" flags as well as the colon syntax for
Expand All @@ -67,12 +58,12 @@ For detailed type information use ignite scaffold type --help

c.Flags().AddFlagSet(flagSetYes())
c.Flags().AddFlagSet(flagSetScaffoldType())
c.Flags().StringSlice(FlagIndexes, []string{"index"}, "fields that index the value")
c.Flags().String(FlagIndexName, "index", "field that index the value")

return c
}

func scaffoldMapHandler(cmd *cobra.Command, args []string) error {
indexes, _ := cmd.Flags().GetStringSlice(FlagIndexes)
return scaffoldType(cmd, args, scaffolder.MapType(indexes...))
index, _ := cmd.Flags().GetString(FlagIndexName)
return scaffoldType(cmd, args, scaffolder.MapType(index))
}
2 changes: 1 addition & 1 deletion ignite/config/plugins/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package plugins

import (
"os"
"slices"
"strings"

"golang.org/x/exp/slices"
"gopkg.in/yaml.v3"

"github.com/ignite/cli/v29/ignite/pkg/errors"
Expand Down
2 changes: 1 addition & 1 deletion ignite/pkg/xstrings/xstrings.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package xstrings

import (
"slices"
"strings"
"unicode"

"golang.org/x/exp/slices" // TODO: replace with slices.Contains when it will be available in stdlib (1.21)
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
Expand Down
33 changes: 20 additions & 13 deletions ignite/services/scaffolder/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type addTypeOptions struct {
isMap bool
isSingleton bool

indexes []string
index string

withoutMessage bool
withoutSimulation bool
Expand All @@ -55,12 +55,11 @@ func ListType() AddTypeKind {
}
}

// MapType makes the type stored in a key-value convention in the storage with a custom
// index option.
func MapType(indexes ...string) AddTypeKind {
// MapType makes the type stored in a key-value convention in the storage with an index option.
func MapType(index string) AddTypeKind {
return func(o *addTypeOptions) {
o.isMap = true
o.indexes = indexes
o.index = index
}
}

Expand Down Expand Up @@ -199,7 +198,7 @@ func (s Scaffolder) AddType(
case o.isList:
g, err = list.NewGenerator(s.Tracer(), opts)
case o.isMap:
g, err = mapGenerator(s.Tracer(), opts, o.indexes)
g, err = mapGenerator(s.Tracer(), opts, o.index)
case o.isSingleton:
g, err = singleton.NewGenerator(s.Tracer(), opts)
default:
Expand Down Expand Up @@ -259,24 +258,32 @@ func parseTypeFields(opts addTypeOptions) (field.Fields, error) {
}

// mapGenerator returns the template generator for a map.
func mapGenerator(replacer placeholder.Replacer, opts *typed.Options, indexes []string) (*genny.Generator, error) {
func mapGenerator(replacer placeholder.Replacer, opts *typed.Options, index string) (*genny.Generator, error) {
// Parse indexes with the associated type
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Parse indexes with the associated type
// Parse index with the associated type

parsedIndexes, err := field.ParseFields(indexes, checkForbiddenTypeIndex)
indexSplit := strings.Split(index, ",")
if len(indexSplit) > 1 {
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
return nil, errors.Errorf("multi-index map isn't supported")
}

parsedIndexes, err := field.ParseFields([]string{index}, checkForbiddenTypeIndex)
if err != nil {
return nil, err
}

if len(parsedIndexes) == 0 {
return nil, errors.Errorf("no index found, a valid map index must be provided")
}

// Indexes and type fields must be disjoint
exists := make(map[string]struct{})
for _, name := range opts.Fields {
exists[name.Name.LowerCamel] = struct{}{}
}
for _, index := range parsedIndexes {
if _, ok := exists[index.Name.LowerCamel]; ok {
return nil, errors.Errorf("%s cannot simultaneously be an index and a field", index.Name.Original)
}

if _, ok := exists[parsedIndexes[0].Name.LowerCamel]; ok {
return nil, errors.Errorf("%s cannot simultaneously be an index and a field", parsedIndexes[0].Name.Original)
}

opts.Indexes = parsedIndexes
opts.Index = parsedIndexes[0]
return maptype.NewGenerator(replacer, opts)
}
4 changes: 2 additions & 2 deletions ignite/services/scaffolder/type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ func TestParseTypeFields(t *testing.T) {
},
{
name: "map type without simulation",
addKind: MapType("foo", "bar"),
addKind: MapType("foo"),
addOptions: []AddTypeOption{
TypeWithoutSimulation(),
},
expectedOptions: addTypeOptions{
moduleName: testModuleName,
indexes: []string{"foo", "bar"},
index: "foo",
isMap: true,
withoutSimulation: true,
signer: testSigner,
Expand Down
9 changes: 7 additions & 2 deletions ignite/templates/field/plushhelpers/plushhelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,20 @@ func ExtendPlushContext(ctx *plush.Context) {
ctx.Set("mergeGoImports", mergeGoImports)
ctx.Set("mergeProtoImports", mergeProtoImports)
ctx.Set("mergeCustomImports", mergeCustomImports)
ctx.Set("appendFieldsAndMergeCustomImports", appendFieldsAndMergeCustomImports)
ctx.Set("title", xstrings.Title)
ctx.Set("toLower", strings.ToLower)
}

func appendFieldsAndMergeCustomImports(f field.Field, fields ...field.Fields) []string {
return mergeCustomImports(append(fields, field.Fields{f})...)
}

func mergeCustomImports(fields ...field.Fields) []string {
allImports := make([]string, 0)
exist := make(map[string]struct{})
for _, fields := range fields {
for _, customImport := range fields.Custom() {
for _, field := range fields {
for _, customImport := range field.Custom() {
if _, ok := exist[customImport]; ok {
continue
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,45 @@ package keeper
import (
"context"

"cosmossdk.io/store/prefix"
"github.com/cosmos/cosmos-sdk/runtime"
"cosmossdk.io/collections"
"github.com/cosmos/cosmos-sdk/types/query"
"<%= ModulePath %>/x/<%= ModuleName %>/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

func (s queryServer) List<%= TypeName.UpperCamel %>(ctx context.Context, req *types.QueryAll<%= TypeName.UpperCamel %>Request) (*types.QueryAll<%= TypeName.UpperCamel %>Response, error) {
func (q queryServer) List<%= TypeName.UpperCamel %>(ctx context.Context, req *types.QueryAll<%= TypeName.UpperCamel %>Request) (*types.QueryAll<%= TypeName.UpperCamel %>Response, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "invalid request")
}

var <%= TypeName.LowerCamel %>s []types.<%= TypeName.UpperCamel %>

store := runtime.KVStoreAdapter(s.k.storeService.OpenKVStore(ctx))
<%= TypeName.LowerCamel %>Store := prefix.NewStore(store, types.KeyPrefix(types.<%= TypeName.UpperCamel %>KeyPrefix))

pageRes, err := query.Paginate(<%= TypeName.LowerCamel %>Store, req.Pagination, func(key []byte, value []byte) error {
var <%= TypeName.LowerCamel %> types.<%= TypeName.UpperCamel %>
if err := s.k.cdc.Unmarshal(value, &<%= TypeName.LowerCamel %>); err != nil {
return err
}

<%= TypeName.LowerCamel %>s = append(<%= TypeName.LowerCamel %>s, <%= TypeName.LowerCamel %>)
return nil
})

<%= TypeName.LowerCamel %>s, pageRes, err := query.CollectionPaginate(
ctx,
q.k.<%= TypeName.UpperCamel %>,
req.Pagination,
func(_ <%= Index.DataType() %>, value types.<%= TypeName.UpperCamel %>) (types.<%= TypeName.UpperCamel %>, error){
return value, nil
},
)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

return &types.QueryAll<%= TypeName.UpperCamel %>Response{<%= TypeName.UpperCamel %>: <%= TypeName.LowerCamel %>s, Pagination: pageRes}, nil
}

func (s queryServer) Get<%= TypeName.UpperCamel %>(ctx context.Context, req *types.QueryGet<%= TypeName.UpperCamel %>Request) (*types.QueryGet<%= TypeName.UpperCamel %>Response, error) {
func (q queryServer) Get<%= TypeName.UpperCamel %>(ctx context.Context, req *types.QueryGet<%= TypeName.UpperCamel %>Request) (*types.QueryGet<%= TypeName.UpperCamel %>Response, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "invalid request")
}

val, found := s.k.Get<%= TypeName.UpperCamel %>(
ctx,
<%= for (i, index) in Indexes { %>req.<%= index.Name.UpperCamel %>,
<% } %>)
if !found {
return nil, status.Error(codes.NotFound, "not found")
val, err := q.k.<%= TypeName.UpperCamel %>.Get(ctx, req.<%= Index.Name.UpperCamel %>)
if err != nil {
if errors.Is(err, collections.ErrNotFound) {
return nil, status.Error(codes.NotFound, "not found")
}

return nil, status.Error(codes.Internal, "internal error")
}

return &types.QueryGet<%= TypeName.UpperCamel %>Response{<%= TypeName.UpperCamel %>: val}, nil
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,23 +1,6 @@
package types

import "encoding/binary"
import "cosmossdk.io/collections"

var _ binary.ByteOrder

const (
// <%= TypeName.UpperCamel %>KeyPrefix is the prefix to retrieve all <%= TypeName.UpperCamel %>
<%= TypeName.UpperCamel %>KeyPrefix = "<%= TypeName.UpperCamel %>/value/"
)

// <%= TypeName.UpperCamel %>Key returns the store key to retrieve a <%= TypeName.UpperCamel %> from the index fields
func <%= TypeName.UpperCamel %>Key(
<%= for (i, index) in Indexes { %><%= index.Name.LowerCamel %> <%= index.DataType() %>,
<% } %>) []byte {
var key []byte
<%= for (i, index) in Indexes { %>
<%= index.ToBytes(index.Name.LowerCamel) %>
key = append(key, <%= index.Name.LowerCamel %>Bytes...)
key = append(key, []byte("/")...)
<% } %>
return key
}
// <%= TypeName.UpperCamel %>Key is the prefix to retrieve all <%= TypeName.UpperCamel %>
var <%= TypeName.UpperCamel %>Key = collections.NewPrefix("<%= TypeName.UpperCamel %>/value/")
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
syntax = "proto3";
package <%= protoPkgName %>;

option go_package = "<%= ModulePath %>/x/<%= ModuleName %>/types";<%= for (importName) in mergeCustomImports(Fields, Indexes) { %>
option go_package = "<%= ModulePath %>/x/<%= ModuleName %>/types";<%= for (importName) in appendFieldsAndMergeCustomImports(Index, Fields) { %>
import "<%= AppName %>/<%= ModuleName %>/<%= importName %>.proto"; <% } %><%= for (importName) in mergeProtoImports(Fields) { %>
import "<%= importName %>"; <% } %>

message <%= TypeName.UpperCamel %> {<%= for (i, index) in Indexes { %>
<%= index.ProtoType(i+1) %>; <% } %><%= for (i, field) in Fields { %>
<%= field.ProtoType(i+1+len(Indexes)) %>; <% } %>
<%= if (!NoMessage) { %>string <%= MsgSigner.LowerCamel %> = <%= len(Fields)+len(Indexes)+1 %>;<% } %>
message <%= TypeName.UpperCamel %> {
<%= Index.ProtoType(1) %>; <%= for (i, field) in Fields { %>
<%= field.ProtoType(i+2) %>; <% } %>
<%= if (!NoMessage) { %>string <%= MsgSigner.LowerCamel %> = <%= len(Fields)+2 %>;<% } %>
}

Loading
Loading