Skip to content

Commit

Permalink
- Context data pass was added
Browse files Browse the repository at this point in the history
- Seeding command was added
- Some small refactoring of migrate command and usage functions implementation in init function (sic!).
  • Loading branch information
smgladkovskiy committed Nov 17, 2021
1 parent c06ef6e commit 5727050
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 29 deletions.
36 changes: 31 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
package commands

import (
"errors"
)

type CommandContextKey string

const (
CommandContextCfgKeyOverall CommandContextKey = "cfg"
CommandContextCfgKeyDB = CommandContextCfgKeyOverall + ".db"
CommandContextCfgKeyLog = CommandContextCfgKeyOverall + ".log"
CommandContextCfgKeyAppInfo = CommandContextCfgKeyOverall + ".appInfo"
CommandContextCfgKeyStage = CommandContextCfgKeyOverall + ".stage"
CommandContextCfgKey CommandContextKey = "cfg"
CommandContextCfgKeyDB = CommandContextCfgKey + ".db"
CommandContextCfgKeyLog = CommandContextCfgKey + ".log"
CommandContextCfgKeyAppInfo = CommandContextCfgKey + ".appInfo"
CommandContextCfgKeyStage = CommandContextCfgKey + ".stage"
)

const (
CommandContextObjectKey CommandContextKey = "obj"
CommandContextObjectKeySeeder = CommandContextObjectKey + ".seeder"
CommandContextObjectKeyConfig = CommandContextObjectKey + ".config"
)

var (
ErrNoMethodFound = errors.New("seed method is not exists")
ErrSeedIsDisabled = errors.New("seed method is disabled in config")
ErrBadContextValue = errors.New("context value is empty or has wrong type")
)

// nolint: gochecknoinits // 🤷
func init() {
MigrateCmd.SetUsageFunc(migrateUsage)
SeedCmd.SetUsageFunc(seedUsage)

SeedCmd.AddCommand(seedRunCmd)
SeedCmd.AddCommand(seedRunAllCmd)
SeedCmd.AddCommand(seedListCmd)
}
44 changes: 20 additions & 24 deletions migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package commands
import (
"context"
"database/sql"
"errors"
"fmt"
"os"

Expand All @@ -18,25 +17,21 @@ import (
)

const (
failureCode = 1
errStrFormat = "%s %s error: %w"
CmdFailureCode = 1
CmdErrStrFormat = "%s %s error: %w"
)

var (
// MigrateCmd is a github.com/pressly/goose database migrate wrapper command.
MigrateCmd = &cobra.Command{
Use: "migrate",
Short: "Database migrations command",
ValidArgs: []string{"up", "up-by-one", "up-to", "create", "down", "down-to", "fix", "redo", "reset", "status", "version"},
Args: cobra.MinimumNArgs(1),
RunE: migrate,
}
ErrBadContextValue = errors.New("context value is empty or has wrong type")
)
// MigrateCmd is a github.com/pressly/goose database migrate wrapper command.
var MigrateCmd = &cobra.Command{
Use: "migrate",
Short: "Database migrations command",
ValidArgs: []string{"up", "up-by-one", "up-to", "create", "down", "down-to", "fix", "redo", "reset", "status", "version"},
Args: cobra.MinimumNArgs(1),
RunE: migrate,
}

// MigrateUsage shows command usage.
// Add it to MigrateCmd like MigrateCmd.SetUsageFunc(MigrateUsage).
func MigrateUsage(cmd *cobra.Command) error {
// migrateUsage shows command usage.
func migrateUsage(cmd *cobra.Command) error {
w := cmd.OutOrStderr()
if _, err := w.Write([]byte(fmt.Sprintf(`Usage:
%s %s [args]
Expand All @@ -54,7 +49,7 @@ Args:
status prints the status of all migrations
version prints the current version of the database
`, cmd.Parent().Name(), cmd.Name()))); err != nil {
return fmt.Errorf("MigrateUsage err: %w", err)
return fmt.Errorf("migrateUsage err: %w", err)
}

return nil
Expand All @@ -66,15 +61,17 @@ func migrate(cmd *cobra.Command, args []string) error {

appStage, dbCfg, logCfg, appInfo, err := getConfigs(cmd.Context())
if err != nil {
return fmt.Errorf(errStrFormat, method, "getConfig", err)
return fmt.Errorf(CmdErrStrFormat, method, "getConfig", err)
}

if err := log.Init(appStage.String(), logCfg, appInfo.GetAlias(), appInfo.GetVersion(), os.Stdout); err != nil {
log.Error().Err(err).Send()

return fmt.Errorf(errStrFormat, method, "log.Init", err)
return fmt.Errorf(CmdErrStrFormat, method, "log.Init", err)
}

log.Info().Msg(appInfo.Summary())

command := args[0]

log.Debug().Str("command", command).Strs("command args", args[0:]).Msg("run migrate command")
Expand All @@ -83,7 +80,7 @@ func migrate(cmd *cobra.Command, args []string) error {
if err != nil {
log.Error().Err(err).Str("dsn", dbCfg.GetMigrationDSN()).Msg("fail to parse config")

return fmt.Errorf(errStrFormat, method, "ParseConfig", err)
return fmt.Errorf(CmdErrStrFormat, method, "ParseConfig", err)
}

cfg.Logger = zerologadapter.NewLogger(log.Logger().With().CallerWithSkipFrameCount(4).Logger()) // nolint:gomnd
Expand All @@ -110,7 +107,7 @@ func migrate(cmd *cobra.Command, args []string) error {
if err := db.Close(); err != nil {
log.Error().Str("dsn", dbCfg.GetDSN()).Err(err).Msg("fail to close DB connection")

os.Exit(failureCode)
os.Exit(CmdFailureCode)
}
}()

Expand Down Expand Up @@ -158,8 +155,7 @@ INSERT INTO %s.%s ("version_id", "is_applied", "tstamp") VALUES ('0', 't', NOW()
if t == nil {
log.Trace().Msg("goose table doesn't exists. let's create it")

_, err := db.Exec(create)
if err != nil {
if _, err := db.Exec(create); err != nil {
return fmt.Errorf("checkInit db.Exec error: %w", err)
}

Expand Down
127 changes: 127 additions & 0 deletions seed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package commands

import (
"context"
"fmt"
"strings"

log "github.com/spacetab-io/logs-go/v2"
"github.com/spf13/cobra"
)

type SeedsInterface interface {
GetMethods() map[string]SeedInterface
GetMethod(name string) (SeedInterface, error)
SeedsList() []string
}

type SeedInterface interface {
Enabled() bool
Name() string
Seed() error
}

// SeedCmd is a database seeding wrapper command.
var (
SeedCmd = &cobra.Command{
Use: "seed",
Short: "Database seeding command",
ValidArgs: []string{"run", "run-all", "list"},
Args: cobra.MinimumNArgs(1),
}
seedListCmd = &cobra.Command{
Use: "list",
RunE: seedList,
}
seedRunAllCmd = &cobra.Command{
Use: "run-all",
RunE: seedRunAll,
}
seedRunCmd = &cobra.Command{
Use: "run",
Args: cobra.MinimumNArgs(1),
RunE: seedRun,
}
)

// seedUsage shows seed command usage.
// Add it to SeedCmd like SeedCmd.SetUsageFunc(seedUsage).
func seedUsage(cmd *cobra.Command) error {
w := cmd.OutOrStderr()
if _, err := w.Write([]byte(fmt.Sprintf(`Usage:
%s %s [args]
Args:
run runs concreete seed
run-all applies all seeds
list shows available seeds list
`, cmd.Parent().Name(), cmd.Name()))); err != nil {
return fmt.Errorf("seedUsage err: %w", err)
}

return nil
}

func seedList(cmd *cobra.Command, _ []string) error {
s, err := getAppSeeder(cmd.Context())
if err != nil {
return err
}

cmd.Printf("Available seed list:\n %s\n", strings.Join(s.SeedsList(), "\n "))

return nil
}

func seedRun(cmd *cobra.Command, args []string) error {
s, err := getAppSeeder(cmd.Context())
if err != nil {
return fmt.Errorf("seedRun getAppSeeder() error: %w", err)
}

log.Trace().Strs("seeds", args).Msg("Running seeder...")

// Execute only the given method names
for _, item := range args {
seed, err := s.GetMethod(item)
if err != nil {
return fmt.Errorf("seedRun GetMethod error: %w", err)
}

if err := seed.Seed(); err != nil {
return fmt.Errorf("seedRun seed.Seed error: %w", err)
}
}

return nil
}

// Execute all seeders if no method name is given.
func seedRunAll(cmd *cobra.Command, _ []string) error {
s, err := getAppSeeder(cmd.Context())
if err != nil {
return fmt.Errorf("seedRunAll getAppSeeder() error: %w", err)
}

log.Trace().Msg("Running all seeder...")

// We are looping over the method on a Seeder struct
for _, seed := range s.GetMethods() {
// Get the method in the current iteration
// Execute seeder
if err := seed.Seed(); err != nil {
return fmt.Errorf("seedRunAll seed.Seed() error: %w", err)
}
}

return nil
}

func getAppSeeder(ctx context.Context) (SeedsInterface, error) {
s, ok := ctx.Value(CommandContextObjectKeySeeder).(SeedsInterface)
if !ok {
return nil, fmt.Errorf("%w: app seed (cfg.appInfo)", ErrBadContextValue)
}

return s, nil
}

0 comments on commit 5727050

Please sign in to comment.