Skip to content

Commit

Permalink
feat: dependencies option for module create (ignite#1287)
Browse files Browse the repository at this point in the history
Co-authored-by: İlker G. Öztürk <ilkergoktugozturk@gmail.com>
  • Loading branch information
lumtis and ilgooz committed Jul 5, 2021
1 parent 7cd66d7 commit f1645b7
Show file tree
Hide file tree
Showing 25 changed files with 789 additions and 194 deletions.
47 changes: 47 additions & 0 deletions integration/cmd_app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,52 @@ func TestGenerateAStargateAppWithEmptyModule(t *testing.T) {
ExecShouldError(),
))

env.Must(env.Exec("create a module with dependencies",
step.NewSteps(step.New(
step.Exec(
"starport",
"s",
"module",
"example_with_dep",
"--dep",
"account,bank,staking,slashing,example",
"--require-registration",
),
step.Workdir(path),
)),
))

env.Must(env.Exec("should prevent creating a module with invalid dependencies",
step.NewSteps(step.New(
step.Exec(
"starport",
"s",
"module",
"example_with_wrong_dep",
"--dep",
"dup,dup",
"--require-registration",
),
step.Workdir(path),
)),
ExecShouldError(),
))

env.Must(env.Exec("should prevent creating a module with a non registered dependency",
step.NewSteps(step.New(
step.Exec(
"starport",
"s",
"module",
"example_with_no_dep",
"--dep",
"inexistent",
"--require-registration",
),
step.Workdir(path),
)),
ExecShouldError(),
))

env.EnsureAppIsSteady(path)
}
52 changes: 48 additions & 4 deletions integration/cmd_ibc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,59 @@ func TestCreateModuleWithIBC(t *testing.T) {

env.Must(env.Exec("create an IBC module with an ordered channel",
step.NewSteps(step.New(
step.Exec("starport", "s", "module", "--ibc", "orderedfoo", "--ordering", "ordered", "--require-registration"),
step.Exec(
"starport",
"s",
"module",
"orderedfoo",
"--ibc",
"--ordering",
"ordered",
"--require-registration",
),
step.Workdir(path),
)),
))

env.Must(env.Exec("create an IBC module with an unordered channel",
step.NewSteps(step.New(
step.Exec("starport", "s", "module", "--ibc", "unorderedfoo", "--ordering", "unordered", "--require-registration"),
step.Exec(
"starport",
"s",
"module",
"unorderedfoo",
"--ibc",
"--ordering",
"unordered",
"--require-registration",
),
step.Workdir(path),
)),
))

env.Must(env.Exec("create an non IBC module",
env.Must(env.Exec("create a non IBC module",
step.NewSteps(step.New(
step.Exec("starport", "s", "module", "foobar", "--require-registration"),
step.Workdir(path),
)),
))

env.Must(env.Exec("create an IBC module with dependencies",
step.NewSteps(step.New(
step.Exec(
"starport",
"s",
"module",
"example_with_dep",
"--ibc",
"--dep",
"account,bank,staking,slashing",
"--require-registration",
),
step.Workdir(path),
)),
))

env.EnsureAppIsSteady(path)
}

Expand All @@ -69,7 +103,17 @@ func TestCreateIBCPacket(t *testing.T) {

env.Must(env.Exec("create a packet",
step.NewSteps(step.New(
step.Exec("starport", "s", "packet", "bar", "text", "--module", "foo", "--ack", "foo:string,bar:int,foobar:bool"),
step.Exec(
"starport",
"s",
"packet",
"bar",
"text",
"--module",
"foo",
"--ack",
"foo:string,bar:int,foobar:bool",
),
step.Workdir(path),
)),
))
Expand Down
26 changes: 24 additions & 2 deletions integration/cmd_message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@ func TestGenerateAnAppWithMessage(t *testing.T) {

env.Must(env.Exec("create a message",
step.NewSteps(step.New(
step.Exec("starport", "s", "message", "do-foo", "text", "vote:int", "like:bool", "-r", "foo,bar:int,foobar:bool"),
step.Exec(
"starport",
"s",
"message",
"do-foo",
"text",
"vote:int",
"like:bool",
"-r",
"foo,bar:int,foobar:bool",
),
step.Workdir(path),
)),
))
Expand Down Expand Up @@ -45,7 +55,19 @@ func TestGenerateAnAppWithMessage(t *testing.T) {

env.Must(env.Exec("create a message in a module",
step.NewSteps(step.New(
step.Exec("starport", "s", "message", "do-foo", "text", "--module", "foo", "--desc", "foo bar foobar", "--response", "foo,bar:int,foobar:bool"),
step.Exec(
"starport",
"s",
"message",
"do-foo",
"text",
"--module",
"foo",
"--desc",
"foo bar foobar",
"--response",
"foo,bar:int,foobar:bool",
),
step.Workdir(path),
)),
))
Expand Down
39 changes: 36 additions & 3 deletions integration/cmd_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,35 @@ func TestGenerateAnAppWithQuery(t *testing.T) {

env.Must(env.Exec("create a query",
step.NewSteps(step.New(
step.Exec("starport", "s", "query", "foo", "text", "vote:int", "like:bool", "-r", "foo,bar:int,foobar:bool"),
step.Exec(
"starport",
"s",
"query",
"foo",
"text",
"vote:int",
"like:bool",
"-r",
"foo,bar:int,foobar:bool",
),
step.Workdir(path),
)),
))

env.Must(env.Exec("create a paginated query",
step.NewSteps(step.New(
step.Exec("starport", "s", "query", "bar", "text", "vote:int", "like:bool", "-r", "foo,bar:int,foobar:bool", "--paginated"),
step.Exec(
"starport",
"s",
"query",
"bar",
"text",
"vote:int",
"like:bool",
"-r",
"foo,bar:int,foobar:bool",
"--paginated",
),
step.Workdir(path),
)),
))
Expand Down Expand Up @@ -50,7 +71,19 @@ func TestGenerateAnAppWithQuery(t *testing.T) {

env.Must(env.Exec("create a query in a module",
step.NewSteps(step.New(
step.Exec("starport", "s", "query", "foo", "text", "--module", "foo", "--desc", "foo bar foobar", "--response", "foo,bar:int,foobar:bool"),
step.Exec(
"starport",
"s",
"query",
"foo",
"text",
"--module",
"foo",
"--desc",
"foo bar foobar",
"--response",
"foo,bar:int,foobar:bool",
),
step.Workdir(path),
)),
))
Expand Down
54 changes: 54 additions & 0 deletions starport/cmd/scaffold_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ import (
"errors"
"fmt"
"io"
"strings"

"github.com/spf13/cobra"
"github.com/tendermint/starport/starport/pkg/clispinner"
"github.com/tendermint/starport/starport/pkg/placeholder"
"github.com/tendermint/starport/starport/pkg/validation"
"github.com/tendermint/starport/starport/services/scaffolder"
"github.com/tendermint/starport/starport/templates/module"
modulecreate "github.com/tendermint/starport/starport/templates/module/create"
)

const (
flagDep = "dep"
flagIBC = "ibc"
flagIBCOrdering = "ordering"
flagRequireRegistration = "require-registration"
Expand Down Expand Up @@ -53,6 +56,7 @@ func NewScaffoldModule() *cobra.Command {
Args: cobra.MinimumNArgs(1),
RunE: scaffoldModuleHandler,
}
c.Flags().StringSlice(flagDep, []string{}, "module dependencies (e.g. --dep account,bank)")
c.Flags().Bool(flagIBC, false, "scaffold an IBC module")
c.Flags().String(flagIBCOrdering, "none", "channel ordering of the IBC module [none|ordered|unordered]")
c.Flags().Bool(flagRequireRegistration, false, "if true command will fail if module can't be registered")
Expand Down Expand Up @@ -86,6 +90,32 @@ func scaffoldModuleHandler(cmd *cobra.Command, args []string) error {
options = append(options, scaffolder.WithIBCChannelOrdering(ibcOrdering), scaffolder.WithIBC())
}

// Get module dependencies
dependencies, err := cmd.Flags().GetStringSlice(flagDep)
if err != nil {
return err
}
if len(dependencies) > 0 {
var formattedDependencies []modulecreate.Dependency

// Parse the provided dependencies
for _, dependency := range dependencies {
var formattedDependency modulecreate.Dependency

splitted := strings.Split(dependency, ":")
switch len(splitted) {
case 1:
formattedDependency = modulecreate.NewDependency(splitted[0], "")
case 2:
formattedDependency = modulecreate.NewDependency(splitted[0], splitted[1])
default:
return fmt.Errorf("dependency %s is invalid, must have <depName> or <depName>.<depKeeperName>", dependency)
}
formattedDependencies = append(formattedDependencies, formattedDependency)
}
options = append(options, scaffolder.WithDependencies(formattedDependencies))
}

sc, err := scaffolder.New(appPath)
if err != nil {
return err
Expand All @@ -111,6 +141,30 @@ func scaffoldModuleHandler(cmd *cobra.Command, args []string) error {
fmt.Println(sourceModificationToString(sm))
}

if len(dependencies) > 0 {
dependencyWarning(dependencies)
}

io.Copy(cmd.OutOrStdout(), &msg)
return nil
}

// in previously scaffolded apps gov keeper is defined below the scaffolded module keeper definition
// therefore we must warn the user to manually move the definition if it's the case
// https://github.com/tendermint/starport/issues/818#issuecomment-865736052
const govWarning = `⚠️ If your app has been scaffolded with Starport 0.16.x or below
Please make sure that your module keeper definition is defined after gov module keeper definition in app/app.go:
app.GovKeeper = ...
...
[your module keeper definition]
`

// dependencyWarning is used to print a warning if gov is provided as a dependency
func dependencyWarning(dependencies []string) {
for _, dep := range dependencies {
if dep == "gov" {
fmt.Print(govWarning)
}
}
}
71 changes: 71 additions & 0 deletions starport/pkg/cosmosanalysis/app/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package app

import (
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"

"github.com/tendermint/starport/starport/pkg/cosmosanalysis"
)

var appImplementation = []string{
"RegisterAPIRoutes",
"RegisterTxService",
"RegisterTendermintService",
}

// CheckKeeper checks for the existence of the keeper with the provided name in the app structure
func CheckKeeper(path, keeperName string) error {
// find app type
appImpl, err := cosmosanalysis.FindImplementation(path, appImplementation)
if err != nil {
return err
}
if len(appImpl) != 1 {
return errors.New("app.go should contain a single app")
}
appTypeName := appImpl[0]

// Inspect the module for app struct
var found bool
fileSet := token.NewFileSet()
pkgs, err := parser.ParseDir(fileSet, path, nil, 0)
if err != nil {
return err
}
for _, pkg := range pkgs {
for _, f := range pkg.Files {
ast.Inspect(f, func(n ast.Node) bool {
// look for struct methods.
appType, ok := n.(*ast.TypeSpec)
if !ok || appType.Name.Name != appTypeName {
return true
}

appStruct, ok := appType.Type.(*ast.StructType)
if !ok {
return true
}

// Search for the keeper specific field
for _, field := range appStruct.Fields.List {
for _, fieldName := range field.Names {
if fieldName.Name == keeperName {
found = true
return false
}
}
}

return false
})
}
}

if !found {
return fmt.Errorf("app doesn't contain %s", keeperName)
}
return nil
}
Loading

0 comments on commit f1645b7

Please sign in to comment.