Skip to content

Commit

Permalink
Generate migrations
Browse files Browse the repository at this point in the history
  • Loading branch information
qbart committed Jan 28, 2023
1 parent af097e2 commit 1b3fcf7
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 23 deletions.
31 changes: 28 additions & 3 deletions krab/action_gen_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"

"github.com/ohkrab/krab/cli"
"github.com/ohkrab/krab/cliargs"
)

// ActionGenMigration generates migration file.
Expand All @@ -26,16 +27,40 @@ func (a *ActionGenMigration) Synopsis() string {

// Run in CLI.
func (a *ActionGenMigration) Run(args []string) int {
resp, err := a.Cmd.Do(context.Background(), CmdOpts{Inputs{"args": args}})
ui := a.Ui
flags := cliargs.New(args)

for _, arg := range a.Cmd.Arguments().Args {
flags.Add(arg.Name)
}

err := flags.Parse()
if err != nil {
ui.Output(a.Help())
ui.Error(err.Error())
return 1
}

resp, err := a.Cmd.Do(context.Background(), CmdOpts{Inputs: flags.Values()})
if err != nil {
a.Ui.Error(err.Error())
return 1
}

response := resp.(ResponseGenMigration)

a.Ui.Output(response.Path)
a.Ui.Output(response.Ref)
a.Ui.Output("File generated:")
a.Ui.Info(response.Path)
a.Ui.Output("Don't forget to add your migration to migration_set:")
a.Ui.Output(`
migration_set "public" {
migrations = [
...`)
a.Ui.Info(fmt.Sprint(" ", response.Ref, ","))
a.Ui.Output(` ...
]
}
`)

return 0
}
2 changes: 1 addition & 1 deletion krab/arguments.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (a *Arguments) Validate(values Inputs) error {
return err
}
} else {
return fmt.Errorf("Argument value for `%s` is missing", a.Name)
return fmt.Errorf("Argument value for `%s` (%s) is missing", a.Name, a.Description)
}
}

Expand Down
44 changes: 36 additions & 8 deletions krab/cmd_gen_migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ import (
"bytes"
"context"
"fmt"
"time"
"path/filepath"

"github.com/ohkrab/krab/krabenv"
"github.com/ohkrab/krab/krabhcl"
"github.com/spf13/afero"
)

// CmdGenMigration generates migation file.
type CmdGenMigration struct {
FS afero.Afero
VersionGenerator
}

// ResponseGenMigration json
Expand All @@ -21,6 +23,18 @@ type ResponseGenMigration struct {
Ref string `json:"ref"`
}

func (c *CmdGenMigration) Arguments() *Arguments {
return &Arguments{
Args: []*Argument{
{
Name: "name",
Type: "string",
Description: "Migration name",
},
},
}
}

func (c *CmdGenMigration) Addr() krabhcl.Addr { return krabhcl.NullAddr }

func (c *CmdGenMigration) Name() []string {
Expand All @@ -30,18 +44,32 @@ func (c *CmdGenMigration) Name() []string {
func (c *CmdGenMigration) HttpMethod() string { return "" }

func (c *CmdGenMigration) Do(ctx context.Context, o CmdOpts) (interface{}, error) {

return c.run(ctx)
err := c.Arguments().Validate(o.Inputs)
if err != nil {
return nil, err
}
return c.run(ctx, o.Inputs)
}

func (c *CmdGenMigration) run(ctx context.Context) (ResponseGenMigration, error) {
func (c *CmdGenMigration) run(ctx context.Context, inputs Inputs) (ResponseGenMigration, error) {
result := ResponseGenMigration{}
buf := bytes.Buffer{}

ref := "create_animals"
dir, err := krabenv.ConfigDir()
if err != nil {
return result, err
}
dir = filepath.Join(dir, "db", "migrations")
err = c.FS.MkdirAll(dir, 0755)
if err != nil {
return result, err
}

version := c.VersionGenerator.Next()
ref := inputs["name"].(string)
result.Ref = fmt.Sprint("migration.", ref)
version := time.Now().UTC().Format("20060102_150405") // YYYYMMDD_HHMMSS
result.Path = filepath.Join(dir, fmt.Sprint(version, "_", ref, krabenv.Ext()))

buf := bytes.Buffer{}
buf.WriteString(`migration "`)
buf.WriteString(ref)
buf.WriteString(`" {`)
Expand All @@ -61,7 +89,7 @@ func (c *CmdGenMigration) run(ctx context.Context) (ResponseGenMigration, error)
buf.WriteString(`}`)
buf.WriteString("\n")

c.FS.WriteFile("/tmp/migrate.krab.hcl", buf.Bytes(), 0644)
c.FS.WriteFile(result.Path, buf.Bytes(), 0644)

return result, nil
}
7 changes: 5 additions & 2 deletions krab/cmd_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (
// CmdRegistry is a list of registred commands.
type CmdRegistry struct {
Commands []Cmd

FS afero.Afero
VersionGenerator
}

// Register appends new command to registry.
Expand All @@ -17,10 +20,10 @@ func (r *CmdRegistry) Register(c Cmd) {
}

// RegisterAll registers all commands in the registry.
func (r *CmdRegistry) RegisterAll(config *Config, fs afero.Afero, conn krabdb.Connection) {
func (r *CmdRegistry) RegisterAll(config *Config, conn krabdb.Connection) {
r.Register(&CmdVersion{})

r.Register(&CmdGenMigration{FS: fs})
r.Register(&CmdGenMigration{FS: r.FS, VersionGenerator: r.VersionGenerator})

for _, action := range config.Actions {
action := action
Expand Down
11 changes: 6 additions & 5 deletions krab/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/hcl2/hclparse"
"github.com/ohkrab/krab/krabenv"
"github.com/ohkrab/krab/krabfn"
"github.com/spf13/afero"
)
Expand Down Expand Up @@ -112,15 +113,15 @@ func (p *Parser) dirFiles(dir string) ([]string, error) {
}

func fileExt(path string) string {
if strings.HasSuffix(path, ".krab.hcl") {
return ".krab.hcl"
if strings.HasSuffix(path, krabenv.Ext()) {
return krabenv.Ext()
}

return "" // unrecognized extension
return "" // unrecognized
}

func isIgnoredFile(name string) bool {
return strings.HasPrefix(name, ".") || // Unix-like hidden files
strings.HasSuffix(name, "~") || // vim
return strings.HasPrefix(name, ".") || // dotfiles
strings.HasSuffix(name, "~") || // vim/backups
strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#") // emacs
}
14 changes: 14 additions & 0 deletions krab/version_generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package krab

import "time"

type VersionGenerator interface {
Next() string
}

type TimestampVersionGenerator struct{}

func (g *TimestampVersionGenerator) Next() string {
version := time.Now().UTC().Format("20060102_150405") // YYYYMMDD_HHMMSS
return version
}
4 changes: 4 additions & 0 deletions krabenv/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ func Env() string {
func Test() bool {
return Env() == "test"
}

func Ext() string {
return ".krab.hcl"
}
8 changes: 6 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ func main() {

conn := &krabdb.DefaultConnection{}

registry := &krab.CmdRegistry{Commands: []krab.Cmd{}}
registry.RegisterAll(config, parser.FS, conn)
registry := &krab.CmdRegistry{
Commands: []krab.Cmd{},
FS: parser.FS,
VersionGenerator: &krab.TimestampVersionGenerator{},
}
registry.RegisterAll(config, conn)
// agent := krabapi.Agent{Registry: registry}
// agent.Run()

Expand Down
35 changes: 35 additions & 0 deletions spec/action_gen_migration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package spec

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

func TestActionGenMigration(t *testing.T) {
c := mockCli(mockConfig(``))
defer c.Teardown()
c.AssertSuccessfulRun(t, []string{"gen", "migration", "-name", "create_maps"})
c.AssertOutputContains(t, "migration.create_maps")
files := c.FSFiles()
assert.Len(t, files, 1)
for k, b := range files {
expected := `migration "create_maps" {
version = "20230101"
up {
}
down {
}
}`
ok, err := c.fs.FileContainsBytes(k, []byte(expected))
assert.NoError(t, err)
if !ok {
fmt.Println("Expected:", expected)
fmt.Println("Current:", string(b))
assert.FailNow(t, "Output file does not contain valid data")
}
}
}
32 changes: 30 additions & 2 deletions spec/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
"io/fs"
"strings"
"testing"

Expand Down Expand Up @@ -40,9 +41,15 @@ func (m *cliMock) setup(args []string) {
m.helpWriter = bytes.Buffer{}
m.uiErrorWriter = bytes.Buffer{}
m.uiWriter = bytes.Buffer{}
memfs := afero.NewMemMapFs()
m.fs = afero.Afero{Fs: memfs}

registry := &krab.CmdRegistry{Commands: []krab.Cmd{}}
registry.RegisterAll(m.config, m.fs, m.connection)
registry := &krab.CmdRegistry{
Commands: []krab.Cmd{},
FS: m.fs,
VersionGenerator: &versionGeneratorMock{},
}
registry.RegisterAll(m.config, m.connection)

m.app = krabcli.New(
cli.New(&m.uiErrorWriter, &m.uiWriter),
Expand Down Expand Up @@ -201,6 +208,21 @@ func (m *cliMock) AssertSQLContains(t *testing.T, expected string) bool {
return assert.True(t, found != -1, fmt.Sprintf("SQL mismatch:\n%s\nwith:\n%s", expected, sql))
}

func (m *cliMock) FSFiles() map[string][]byte {
data := map[string][]byte{}
m.fs.Walk("/", func(path string, info fs.FileInfo, err error) error {
if !info.IsDir() {
b, err := m.fs.ReadFile(path)
if err != nil {
panic(err)
}
data[path] = b
}
return nil
})
return data
}

func (m *cliMock) ResetSQLRecorder() {
m.connection.assertedSQLIndex = 0
m.connection.recorder = []string{}
Expand Down Expand Up @@ -240,6 +262,12 @@ func (m *cliMock) Insert(t *testing.T, table string, cols string, vals string) b
return assert.NoError(t, err, "Insertion must happen")
}

type versionGeneratorMock struct{}

func (g *versionGeneratorMock) Next() string {
return "20230101"
}

func mockCli(config *krab.Config) *cliMock {
mock := &cliMock{
config: config,
Expand Down

0 comments on commit 1b3fcf7

Please sign in to comment.