Skip to content

Commit

Permalink
feat: Add MigrationService (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
allisson committed Mar 4, 2021
1 parent dcba41f commit 88cf060
Show file tree
Hide file tree
Showing 13 changed files with 823 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -22,3 +22,6 @@ bin/
# golang migrate
migrate.darwin-amd64
migrate.linux-amd64

# dot env
.env
63 changes: 63 additions & 0 deletions cmd/postmand/main.go
@@ -0,0 +1,63 @@
package main

import (
"log"
"os"

"github.com/allisson/go-env"
"github.com/allisson/postmand/repository"
"github.com/allisson/postmand/service"
"github.com/jmoiron/sqlx"
_ "github.com/joho/godotenv/autoload"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
)

func main() {
// Setup logger
logger, err := zap.NewProduction()
if err != nil {
log.Fatalf("logger-setup-error: %v\n", err)
}
// nolint:errcheck
defer logger.Sync()

// Setup postgresql database
db, err := sqlx.Open("postgres", env.GetString("POSTMAND_DATABASE_URL", ""))
if err != nil {
logger.Fatal("database-setup-error", zap.Error(err))
}
if err := db.Ping(); err != nil {
logger.Fatal("database-ping-error", zap.Error(err))
}

// Setup cli
app := cli.NewApp()
app.Name = "postmand"
app.Usage = "CLI"
app.Authors = []*cli.Author{
{
Name: "Allisson Azevedo",
Email: "allisson@gmail.com",
},
}
app.Commands = []*cli.Command{
{
Name: "migrate",
Aliases: []string{"m"},
Usage: "execute database migration",
Action: func(c *cli.Context) error {
migrationRepository := repository.NewMigration(
db,
env.GetString("POSTMAND_DATABASE_MIGRATION_DIR", "file:///db/migrations"),
)
migrationService := service.NewMigration(migrationRepository)
return migrationService.Run(c.Context)
},
},
}
err = app.Run(os.Args)
if err != nil {
logger.Fatal("cli-error", zap.Error(err))
}
}
4 changes: 4 additions & 0 deletions go.mod
Expand Up @@ -4,12 +4,16 @@ go 1.16

require (
github.com/DATA-DOG/go-txdb v0.1.3
github.com/allisson/go-env v0.3.0
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/golang-migrate/migrate/v4 v4.14.1
github.com/google/uuid v1.2.0
github.com/huandu/go-sqlbuilder v1.12.0
github.com/jmoiron/sqlx v1.3.1
github.com/joho/godotenv v1.3.0
github.com/jpillora/backoff v1.0.0
github.com/lib/pq v1.9.0
github.com/stretchr/testify v1.7.0
github.com/urfave/cli/v2 v2.3.0
go.uber.org/zap v1.16.0
)
580 changes: 574 additions & 6 deletions go.sum

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions local.env
@@ -0,0 +1,3 @@
POSTMAND_DATABASE_URL='postgres://user:pass@localhost:5432/postmand?sslmode=disable'
# See https://github.com/golang-migrate/migrate/tree/master/source/file
POSTMAND_DATABASE_MIGRATION_DIR='file://db/migrations'
28 changes: 28 additions & 0 deletions mocks/MigrationRepository.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions mocks/MigrationService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions repository.go
Expand Up @@ -41,3 +41,8 @@ type DeliveryAttemptRepository interface {
List(ctx context.Context, listOptions RepositoryListOptions) ([]*DeliveryAttempt, error)
Create(ctx context.Context, deliveryAttempt *DeliveryAttempt) error
}

// MigrationRepository is the interface that will be used to run database migrations.
type MigrationRepository interface {
Run(ctx context.Context) error
}
50 changes: 50 additions & 0 deletions repository/migration.go
@@ -0,0 +1,50 @@
package repository

import (
"context"
"fmt"

"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/postgres"

// For migrate with files
_ "github.com/golang-migrate/migrate/v4/source/file"
"github.com/jmoiron/sqlx"
)

// Migration implements postmand.MigrationRepository interface.
type Migration struct {
db *sqlx.DB
migrationDir string
}

// Run migrations
func (m *Migration) Run(ctx context.Context) error {
driver, err := postgres.WithInstance(m.db.DB, &postgres.Config{})
if err != nil {
return err
}

mg, err := migrate.NewWithDatabaseInstance(m.migrationDir, "postgres", driver)
if err != nil {
fmt.Println(err)
return err
}

err = mg.Up()
if err != nil {
if err != migrate.ErrNoChange {
return err
}
}

return nil
}

// NewMigration will create an implementation of postmand.MigrationRepository.
func NewMigration(db *sqlx.DB, migrationDir string) *Migration {
return &Migration{
db: db,
migrationDir: migrationDir,
}
}
18 changes: 18 additions & 0 deletions repository/migration_test.go
@@ -0,0 +1,18 @@
package repository

import (
"context"
"testing"

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

func TestMigration(t *testing.T) {
ctx := context.Background()
th := newTestHelper()
defer th.db.Close()
migrationDir := "file://../db/migrations"
migration := NewMigration(th.db, migrationDir)
err := migration.Run(ctx)
assert.Nil(t, err)
}
5 changes: 5 additions & 0 deletions service.go
Expand Up @@ -7,3 +7,8 @@ type WorkerService interface {
Run(ctx context.Context)
Shutdown(ctx context.Context)
}

// MigrationService is the interface that will be used to execute database migrations.
type MigrationService interface {
Run(ctx context.Context) error
}
22 changes: 22 additions & 0 deletions service/migration.go
@@ -0,0 +1,22 @@
package service

import (
"context"

"github.com/allisson/postmand"
)

// Migration implements postmand.MigrationService interface.
type Migration struct {
migrationRepo postmand.MigrationRepository
}

// Run database migrations.
func (m Migration) Run(ctx context.Context) error {
return m.migrationRepo.Run(ctx)
}

// NewMigration will create an implementation of postmand.MigrationService.
func NewMigration(migrationRepo postmand.MigrationRepository) *Migration {
return &Migration{migrationRepo: migrationRepo}
}
20 changes: 20 additions & 0 deletions service/migration_test.go
@@ -0,0 +1,20 @@
package service

import (
"context"
"testing"

"github.com/allisson/postmand/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func TestMigration(t *testing.T) {
migrationRepository := &mocks.MigrationRepository{}
migrationService := NewMigration(migrationRepository)
migrationRepository.On("Run", mock.Anything).Return(nil)
ctx := context.Background()
err := migrationService.Run(ctx)
assert.Nil(t, err)
migrationRepository.AssertExpectations(t)
}

0 comments on commit 88cf060

Please sign in to comment.