Skip to content

Commit

Permalink
Custom action foundation
Browse files Browse the repository at this point in the history
  • Loading branch information
qbart committed Dec 1, 2021
1 parent d88e899 commit eee54d7
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 7 deletions.
43 changes: 43 additions & 0 deletions krab/action_custom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package krab

import (
"context"
"fmt"

"github.com/ohkrab/krab/cli"
"github.com/ohkrab/krab/krabdb"
"github.com/ohkrab/krab/tpls"
)

// ActionCustom keeps data needed to perform this action.
type ActionCustom struct {
Ui cli.UI
Action *Action
Arguments Arguments
Connection krabdb.Connection
}

func (a *ActionCustom) Help() string {
return fmt.Sprint(
`Usage: krab action namespace name`,
"\n\n",
a.Arguments.Help(),
`
Performs custom action.
`,
)
}

func (a *ActionCustom) Synopsis() string {
return fmt.Sprintf("Action")
}

// Run in CLI.
func (a *ActionCustom) Run(args []string) int {
return 0
}

// Do performs the action.
func (a *ActionCustom) Do(ctx context.Context, db krabdb.DB, tpl *tpls.Templates) error {
return nil
}
9 changes: 7 additions & 2 deletions krab/addr.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ type Addr struct {
}

// String returns full reference name including the keyword.
func (a *Addr) String() string {
func (a Addr) String() string {
return fmt.Sprintf("%s.%s", a.Keyword, a.OnlyRefNames())
}

// Absolute returns keyword and labels as a single slice.
func (a Addr) Absolute() []string {
return append([]string{a.Keyword}, a.Labels...)
}

// OnlyRefNames returns reference name without the keyword.
func (a *Addr) OnlyRefNames() string {
func (a Addr) OnlyRefNames() string {
return strings.Join(a.Labels, ".")
}

Expand Down
16 changes: 13 additions & 3 deletions krab/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import (
type Config struct {
MigrationSets map[string]*MigrationSet
Migrations map[string]*Migration
Actions map[string]*Action
}

// NewConfig returns new configuration that was read from Parser.
// Transient attributes are updated with parsed data.
func NewConfig(files []*File) (*Config, error) {
c := &Config{
MigrationSets: make(map[string]*MigrationSet),
Migrations: make(map[string]*Migration),
MigrationSets: map[string]*MigrationSet{},
Migrations: map[string]*Migration{},
Actions: map[string]*Action{},
}

// append files
Expand All @@ -30,7 +32,7 @@ func NewConfig(files []*File) (*Config, error) {

// parse refs
for _, set := range c.MigrationSets {
set.Migrations = make([]*Migration, 0)
set.Migrations = []*Migration{}

traversals := set.MigrationsExpr.Variables()
for _, t := range traversals {
Expand Down Expand Up @@ -88,6 +90,14 @@ func (c *Config) appendFile(file *File) error {
c.MigrationSets[s.RefName] = s
}

for _, a := range file.Actions {
if _, found := c.Actions[a.Addr().OnlyRefNames()]; found {
return fmt.Errorf("Action with the name '%s' already exists", a.Addr().OnlyRefNames())
}

c.Actions[a.Addr().OnlyRefNames()] = a
}

return nil
}

Expand Down
1 change: 1 addition & 0 deletions krab/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "github.com/hashicorp/hcl/v2"
type File struct {
Migrations []*Migration `hcl:"migration,block"`
MigrationSets []*MigrationSet `hcl:"migration_set,block"`
Actions []*Action `hcl:"action,block"`

Raw *RawFile
}
Expand Down
29 changes: 29 additions & 0 deletions krab/type_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package krab

import (
"io"
)

// Action represents custom action to execute.
//
type Action struct {
Namespace string `hcl:"namespace,label"`
RefName string `hcl:"ref_name,label"`

SQL string `hcl:"sql"`
}

func (a *Action) Addr() Addr {
return Addr{Keyword: "action", Labels: []string{a.Namespace, a.RefName}}
}

func (a *Action) Validate() error {
return ErrorCoalesce(
ValidateRefName(a.Namespace),
ValidateRefName(a.RefName),
)
}

func (m *Action) ToSQL(w io.StringWriter) {
w.WriteString(m.SQL)
}
2 changes: 0 additions & 2 deletions krab/type_migration.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package krab

import (
"fmt"
"io"

"github.com/hashicorp/hcl/v2"
Expand Down Expand Up @@ -54,7 +53,6 @@ type RawMigrationUpOrDown struct {
func (ms *Migration) Validate() error {
return ErrorCoalesce(
ValidateRefName(ms.RefName),
ValidateStringNonEmpty(fmt.Sprint("`version` attribute in `", ms.RefName, "` migration"), ms.Version),
ms.Up.Validate(),
ms.Down.Validate(),
)
Expand Down
8 changes: 8 additions & 0 deletions krabcli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package krabcli

import (
"fmt"
"strings"

mcli "github.com/mitchellh/cli"
"github.com/ohkrab/krab/cli"
Expand Down Expand Up @@ -44,6 +45,13 @@ func (a *App) RegisterAll() {
return &krab.ActionVersion{Ui: a.Ui}
})

for _, action := range a.Config.Actions {
localAction := action
a.RegisterCmd(strings.Join(action.Addr().Absolute(), " "), func() Command {
return &krab.ActionCustom{Ui: a.Ui, Action: localAction, Connection: a.connection}
})
}

for _, set := range a.Config.MigrationSets {
localSet := set

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

import (
"testing"

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

func TestActionCustom(t *testing.T) {
c := mockCli(mockConfig(`
migration "create_animals" {
version = "v1"
up { sql = "CREATE TABLE animals(name VARCHAR)" }
down { sql = "DROP TABLE animals" }
}
migration "create_animals_view" {
version = "v2"
up { sql = "CREATE MATERIALIZED VIEW anims AS SELECT name FROM animals" }
down { sql = "DROP VIEW anims" }
}
migration "seed_animals" {
version = "v3"
up { sql = "INSERT INTO animals(name) VALUES('Elephant'),('Turtle'),('Cat')" }
down { sql = "TRUNACTE animals" }
}
migration_set "animals" {
migrations = [
migration.create_animals,
migration.create_animals_view,
migration.seed_animals,
]
}
action "view" "refresh" {
sql = "REFRESH VIEW anims"
}
`))
defer c.Teardown()

c.AssertSuccessfulRun(t, []string{"migrate", "up", "animals"})
c.AssertSchemaMigrationTable(t, "public", "v1", "v2", "v3")

_, vals := c.Query(t, "SELECT * FROM anims")
if assert.Len(t, vals, 0, "No values should be returned") {
c.AssertSuccessfulRun(t, []string{"action", "view", "refresh"})
_, vals := c.Query(t, "SELECT * FROM anims")
assert.Len(t, vals, 3, "There should be 3 animals after refresh")
}

}

0 comments on commit eee54d7

Please sign in to comment.