- redefined mongo db driver to be autonomous and not depending on specific methods

- defined methods receivers registration by name and change the migration files format to include them
- added extensive testing
This commit is contained in:
dimag 2016-08-09 16:47:34 +03:00
parent c71fea90d6
commit 570e4b42be
8 changed files with 462 additions and 412 deletions

View File

@ -2,22 +2,30 @@ package gomethods
import (
//"bytes"
"reflect"
"bufio"
"database/sql/driver"
"fmt"
"strings"
"github.com/dimag-jfrog/migrate/file"
"os"
"path"
"bufio"
"github.com/dimag-jfrog/migrate/file"
"strings"
)
type UnregisteredMethodsReceiverError string
func (e UnregisteredMethodsReceiverError) Error() string {
return "Unregistered methods receiver: " + string(e)
}
type MissingMethodError string
func (e MissingMethodError) Error() string { return "Non existing migrate method: " + string(e) }
type WrongMethodSignatureError string
func (e WrongMethodSignatureError) Error() string { return fmt.Sprintf("Method %s has wrong signature", e) }
func (e WrongMethodSignatureError) Error() string {
return fmt.Sprintf("Method %s has wrong signature", e)
}
type MethodInvocationFailedError struct {
MethodName string
@ -28,14 +36,23 @@ func (e *MethodInvocationFailedError) Error() string {
return fmt.Sprintf("Method %s returned an error: %v", e.MethodName, e.Error)
}
type MigrationMethodInvoker interface {
IsValid(methodName string, methodReceiver interface{}) bool
Invoke(methodName string, methodReceiver interface{}) error
}
type GoMethodsDriver interface {
driver.Driver
MigrationMethodInvoker
}
type Migrator struct {
MigrationMethodsReceiver interface{}
RollbackOnFailure bool
MethodInvoker MigrationMethodInvoker
}
func (m *Migrator) Migrate(f file.File, pipe chan interface{}) error {
methods, err := m.getMigrationMethods(f)
methods, methodsReceiver, err := m.getMigrationMethods(f)
if err != nil {
pipe <- err
return err
@ -43,7 +60,7 @@ func (m *Migrator) Migrate(f file.File, pipe chan interface{}) error {
for i, methodName := range methods {
pipe <- methodName
err := m.Invoke(methodName)
err := m.MethodInvoker.Invoke(methodName, methodsReceiver)
if err != nil {
pipe <- err
if !m.RollbackOnFailure {
@ -53,12 +70,13 @@ func (m *Migrator) Migrate(f file.File, pipe chan interface{}) error {
// on failure, try to rollback methods in this migration
for j := i - 1; j >= 0; j-- {
rollbackToMethodName := getRollbackToMethod(methods[j])
if rollbackToMethodName == "" || !m.IsValid(rollbackToMethodName) {
if rollbackToMethodName == "" ||
!m.MethodInvoker.IsValid(rollbackToMethodName, methodsReceiver) {
continue
}
pipe <- rollbackToMethodName
err = m.Invoke(rollbackToMethodName)
err = m.MethodInvoker.Invoke(rollbackToMethodName, methodsReceiver)
if err != nil {
pipe <- err
break
@ -71,33 +89,6 @@ func (m *Migrator) Migrate(f file.File, pipe chan interface{}) error {
return nil
}
func (m *Migrator) IsValid(methodName string) bool {
return reflect.ValueOf(m.MigrationMethodsReceiver).MethodByName(methodName).IsValid()
}
func (m *Migrator) Invoke(methodName string) error {
name := methodName
migrateMethod := reflect.ValueOf(m.MigrationMethodsReceiver).MethodByName(name)
if !migrateMethod.IsValid() {
return MissingMethodError(methodName)
}
retValues := migrateMethod.Call([]reflect.Value{})
if len(retValues) != 1 {
return WrongMethodSignatureError(name)
}
if !retValues[0].IsNil() {
err, ok := retValues[0].Interface().(error)
if !ok {
return WrongMethodSignatureError(name)
}
return &MethodInvocationFailedError{ MethodName:name, Err:err}
}
return nil
}
func reverseInPlace(a []string) {
for i := 0; i < len(a)/2; i++ {
j := len(a) - i - 1
@ -139,28 +130,40 @@ func getFileLines(file file.File) ([]string, error) {
}
}
func (m *Migrator) getMigrationMethods(f file.File) ([]string, error) {
var lines, methods []string
lines, err := getFileLines(f)
func (m *Migrator) getMigrationMethods(f file.File) (methods []string, methodsReceiver interface{}, err error) {
var lines []string
lines, err = getFileLines(f)
if err != nil {
return nil, err
return nil, nil, err
}
for _, line := range lines {
methodName := strings.TrimSpace(line)
line := strings.TrimSpace(line)
if methodName == "" || strings.HasPrefix(methodName, "--") {
if line == "" || strings.HasPrefix(line, "--") {
// an empty line or a comment, ignore
continue
}
if !m.IsValid(methodName) {
return nil, MissingMethodError(methodName)
if methodsReceiver == nil {
receiverName := line
methodsReceiver = GetMethodsReceiver(receiverName)
if methodsReceiver == nil {
return nil, nil, UnregisteredMethodsReceiverError(receiverName)
}
continue
} else {
methodName := line
if !m.MethodInvoker.IsValid(methodName, methodsReceiver) {
return nil, nil, MissingMethodError(methodName)
}
methods = append(methods, methodName)
}
}
return methods, nil
return methods, methodsReceiver, nil
}

View File

@ -10,67 +10,35 @@ import (
pipep "github.com/dimag-jfrog/migrate/pipe"
)
type FakeGoMethodsDriver struct {
type FakeGoMethodsInvoker struct {
InvokedMethods []string
Migrator Migrator
}
func (driver *FakeGoMethodsDriver) Initialize(url string) error {
func (invoker *FakeGoMethodsInvoker) IsValid(methodName string, methodReceiver interface{}) bool {
if methodName == "V001_some_non_existing_method_up" {
return false
}
return true
}
func (invoker *FakeGoMethodsInvoker) Invoke(methodName string, methodReceiver interface{}) error {
invoker.InvokedMethods = append(invoker.InvokedMethods, methodName)
if methodName == "V001_some_failing_method_up" || methodName == "V001_some_failing_method_down" {
return &MethodInvocationFailedError{
MethodName: methodName,
Err: SomeError{},
}
} else {
return nil
}
func (driver *FakeGoMethodsDriver) Close() error {
return nil
}
func (driver *FakeGoMethodsDriver) FilenameExtension() string {
return "gm"
}
func (driver *FakeGoMethodsDriver) Version() (uint64, error) {
return uint64(0), nil
}
func (driver *FakeGoMethodsDriver) Migrate(f file.File, pipe chan interface{}) {
defer close(pipe)
pipe <- f
return
}
func (driver *FakeGoMethodsDriver) V001_init_organizations_up() error {
driver.InvokedMethods = append(driver.InvokedMethods, "V001_init_organizations_up")
return nil
}
func (driver *FakeGoMethodsDriver) V001_init_organizations_down() error {
driver.InvokedMethods = append(driver.InvokedMethods, "V001_init_organizations_down")
return nil
}
func (driver *FakeGoMethodsDriver) V001_init_users_up() error {
driver.InvokedMethods = append(driver.InvokedMethods, "V001_init_users_up")
return nil
}
func (driver *FakeGoMethodsDriver) V001_init_users_down() error {
driver.InvokedMethods = append(driver.InvokedMethods, "V001_init_users_down")
return nil
}
type SomeError struct{}
func (e SomeError) Error() string { return "Some error happened" }
func (driver *FakeGoMethodsDriver) V001_some_failing_method_up() error {
driver.InvokedMethods = append(driver.InvokedMethods, "V001_some_failing_method_up")
return SomeError{}
}
func (driver *FakeGoMethodsDriver) V001_some_failing_method_down() error {
driver.InvokedMethods = append(driver.InvokedMethods, "V001_some_failing_method_down")
return SomeError{}
}
func TestMigrate(t *testing.T) {
cases := []struct {
name string
@ -88,6 +56,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
FakeMethodsReceiver
V001_init_organizations_up
V001_init_users_up
`),
@ -104,6 +73,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Down,
Content: []byte(`
FakeMethodsReceiver
V001_init_users_down
V001_init_organizations_down
`),
@ -120,6 +90,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
FakeMethodsReceiver
V001_init_organizations_up
V001_init_users_up
V001_some_non_existing_method_up
@ -137,6 +108,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
FakeMethodsReceiver
V001_init_organizations_up
V001_some_failing_method_up
V001_init_users_up
@ -160,6 +132,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Down,
Content: []byte(`
FakeMethodsReceiver
V001_init_users_down
V001_some_failing_method_down
V001_init_organizations_down
@ -184,6 +157,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
FakeMethodsReceiver
V001_init_organizations_up
V001_init_users_up
V001_some_failing_method_up
@ -211,6 +185,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Down,
Content: []byte(`
FakeMethodsReceiver
V001_init_users_down
V001_some_failing_method_down
V001_init_organizations_down
@ -226,13 +201,15 @@ func TestMigrate(t *testing.T) {
Err: SomeError{},
}},
},
}
RegisterMethodsReceiver("FakeMethodsReceiver", "")
for _, c := range cases {
migrator := Migrator{}
d := &FakeGoMethodsDriver{Migrator: migrator, InvokedMethods:[]string{}}
migrator.MigrationMethodsReceiver = d
fakeInvoker := &FakeGoMethodsInvoker{InvokedMethods: []string{}}
migrator.MethodInvoker = fakeInvoker
migrator.RollbackOnFailure = c.expectRollback
pipe := pipep.New()
@ -243,9 +220,9 @@ func TestMigrate(t *testing.T) {
errs := pipep.ReadErrors(pipe)
var failed bool
if !reflect.DeepEqual(d.InvokedMethods, c.expectedInvokedMethods) {
if !reflect.DeepEqual(fakeInvoker.InvokedMethods, c.expectedInvokedMethods) {
failed = true
t.Errorf("case '%s': FAILED\nexpected invoked methods %v\nbut got %v", c.name, c.expectedInvokedMethods, d.InvokedMethods)
t.Errorf("case '%s': FAILED\nexpected invoked methods %v\nbut got %v", c.name, c.expectedInvokedMethods, fakeInvoker.InvokedMethods)
}
if !reflect.DeepEqual(errs, c.expectedErrors) {
failed = true
@ -258,8 +235,6 @@ func TestMigrate(t *testing.T) {
}
}
func TestGetRollbackToMethod(t *testing.T) {
cases := []struct {
method string
@ -304,4 +279,3 @@ func TestReverseInPlace(t *testing.T) {
t.Errorf("Expected reverse methods %v but got %v", expectedReversedMethods, methods)
}
}

View File

@ -0,0 +1,30 @@
package gomethods
import (
"sync"
)
var methodsReceiversMu sync.Mutex
var methodsReceivers = make(map[string]interface{})
// Registers a methods receiver object so it can be created from its name. Users of gomethods migration drivers should
// call this method to register objects with their migration methods before executing the migration
func RegisterMethodsReceiver(name string, receiver interface{}) {
methodsReceiversMu.Lock()
defer methodsReceiversMu.Unlock()
if receiver == nil {
panic("Go methods: Register receiver object is nil")
}
if _, dup := methodsReceivers[name]; dup {
panic("Go methods: Register called twice for receiver " + name)
}
methodsReceivers[name] = receiver
}
// Retrieves a registered driver by name
func GetMethodsReceiver(name string) interface{} {
methodsReceiversMu.Lock()
defer methodsReceiversMu.Unlock()
receiver := methodsReceivers[name]
return receiver
}

View File

@ -0,0 +1,134 @@
package mongodb
import (
"errors"
"github.com/dimag-jfrog/migrate/driver"
"github.com/dimag-jfrog/migrate/driver/gomethods"
"github.com/dimag-jfrog/migrate/file"
"github.com/dimag-jfrog/migrate/migrate/direction"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"reflect"
"strings"
)
const MIGRATE_DB = "db_migrations"
const MIGRATE_C = "db_migrations"
type MongoDbGoMethodsDriver struct {
Session *mgo.Session
DbName string
migrator gomethods.Migrator
}
func init() {
driver.RegisterDriver("mongodb", &MongoDbGoMethodsDriver{})
}
type DbMigration struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Version uint64 `bson:"version"`
}
func (driver *MongoDbGoMethodsDriver) Initialize(url string) error {
urlWithoutScheme := strings.SplitN(url, "mongodb://", 2)
if len(urlWithoutScheme) != 2 {
return errors.New("invalid mongodb:// scheme")
}
session, err := mgo.Dial(url)
if err != nil {
return err
}
session.SetMode(mgo.Monotonic, true)
driver.Session = session
driver.DbName = MIGRATE_DB
driver.migrator = gomethods.Migrator{MethodInvoker: driver}
return nil
}
func (driver *MongoDbGoMethodsDriver) Close() error {
if driver.Session != nil {
driver.Session.Close()
}
return nil
}
func (driver *MongoDbGoMethodsDriver) FilenameExtension() string {
return "mgo"
}
func (driver *MongoDbGoMethodsDriver) Version() (uint64, error) {
var latestMigration DbMigration
c := driver.Session.DB(driver.DbName).C(MIGRATE_C)
err := c.Find(bson.M{}).Sort("-version").One(&latestMigration)
switch {
case err == mgo.ErrNotFound:
return 0, nil
case err != nil:
return 0, err
default:
return latestMigration.Version, nil
}
}
func (driver *MongoDbGoMethodsDriver) Migrate(f file.File, pipe chan interface{}) {
defer close(pipe)
pipe <- f
err := driver.migrator.Migrate(f, pipe)
if err != nil {
return
}
migrate_c := driver.Session.DB(driver.DbName).C(MIGRATE_C)
if f.Direction == direction.Up {
id := bson.NewObjectId()
dbMigration := DbMigration{Id: id, Version: f.Version}
err := migrate_c.Insert(dbMigration)
if err != nil {
pipe <- err
return
}
} else if f.Direction == direction.Down {
err := migrate_c.Remove(bson.M{"version": f.Version})
if err != nil {
pipe <- err
return
}
}
}
func (driver *MongoDbGoMethodsDriver) IsValid(methodName string, methodsReceiver interface{}) bool {
return reflect.ValueOf(methodsReceiver).MethodByName(methodName).IsValid()
}
func (driver *MongoDbGoMethodsDriver) Invoke(methodName string, methodsReceiver interface{}) error {
name := methodName
migrateMethod := reflect.ValueOf(methodsReceiver).MethodByName(name)
if !migrateMethod.IsValid() {
return gomethods.MissingMethodError(methodName)
}
retValues := migrateMethod.Call([]reflect.Value{reflect.ValueOf(driver.Session)})
if len(retValues) != 1 {
return gomethods.WrongMethodSignatureError(name)
}
if !retValues[0].IsNil() {
err, ok := retValues[0].Interface().(error)
if !ok {
return gomethods.WrongMethodSignatureError(name)
}
return &gomethods.MethodInvocationFailedError{MethodName: name, Err: err}
}
return nil
}

View File

@ -1,107 +0,0 @@
package mongodb
import (
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"errors"
"strings"
"github.com/dimag-jfrog/migrate/migrate/direction"
"github.com/dimag-jfrog/migrate/file"
"github.com/dimag-jfrog/migrate/driver/gomethods"
)
const MIGRATE_C = "db_migrations"
// This is not a real driver since the Initialize method requires a gomethods.Migrator
// The real driver will contain the DriverTemplate and implement all the custom migration Golang methods
// See example in usage_examples for details
type DriverTemplate struct {
Session *mgo.Session
DbName string
migrator gomethods.Migrator
}
type DbMigration struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Version uint64 `bson:"version"`
}
func (driver *DriverTemplate) Initialize(url, dbName string, migrator gomethods.Migrator) error {
urlWithoutScheme := strings.SplitN(url, "mongodb://", 2)
if len(urlWithoutScheme) != 2 {
return errors.New("invalid mongodb:// scheme")
}
session, err := mgo.Dial(url)
if err != nil {
return err
}
session.SetMode(mgo.Monotonic, true)
driver.Session = session
driver.DbName = dbName
driver.migrator = migrator
return nil
}
func (driver *DriverTemplate) Close() error {
if driver.Session != nil {
driver.Session.Close()
}
return nil
}
func (driver *DriverTemplate) FilenameExtension() string {
return "mgo"
}
func (driver *DriverTemplate) Version() (uint64, error) {
var latestMigration DbMigration
c := driver.Session.DB(driver.DbName).C(MIGRATE_C)
err := c.Find(bson.M{}).Sort("-version").One(&latestMigration)
switch {
case err == mgo.ErrNotFound:
return 0, nil
case err != nil:
return 0, err
default:
return latestMigration.Version, nil
}
}
func (driver *DriverTemplate) Migrate(f file.File, pipe chan interface{}) {
defer close(pipe)
pipe <- f
err := driver.migrator.Migrate(f, pipe)
if err != nil {
return
}
migrate_c := driver.Session.DB(driver.DbName).C(MIGRATE_C)
if f.Direction == direction.Up {
id := bson.NewObjectId()
dbMigration := DbMigration{Id: id, Version: f.Version}
err := migrate_c.Insert(dbMigration)
if err != nil {
pipe <- err
return
}
} else if f.Direction == direction.Down {
err := migrate_c.Remove(bson.M{"version": f.Version})
if err != nil {
pipe <- err
return
}
}
}

View File

@ -0,0 +1,129 @@
package mongodb_example
import (
"github.com/dimag-jfrog/migrate/driver/gomethods"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"time"
)
type MyMgoMethodsReceiver struct {
}
func init() {
gomethods.RegisterMethodsReceiver("MyMgoMethodsReceiver", &MyMgoMethodsReceiver{})
}
// Here goes the specific mongodb golang methods driver logic
const DB_NAME = "test"
const SHORT_DATE_LAYOUT = "2000-Jan-01"
const USERS_C = "users"
const ORGANIZATIONS_C = "organizations"
type Organization struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Name string `bson:"name"`
Location string `bson:"location"`
DateFounded time.Time `bson:"date_founded"`
}
type Organization_v2 struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Name string `bson:"name"`
Headquarters string `bson:"headquarters"`
DateFounded time.Time `bson:"date_founded"`
}
type User struct {
Id bson.ObjectId `bson:"_id"`
Name string `bson:"name"`
}
var OrganizationIds []bson.ObjectId = []bson.ObjectId{
bson.NewObjectId(),
bson.NewObjectId(),
bson.NewObjectId(),
}
var UserIds []bson.ObjectId = []bson.ObjectId{
bson.NewObjectId(),
bson.NewObjectId(),
bson.NewObjectId(),
}
func (r *MyMgoMethodsReceiver) V001_init_organizations_up(session *mgo.Session) error {
date1, _ := time.Parse(SHORT_DATE_LAYOUT, "1994-Jul-05")
date2, _ := time.Parse(SHORT_DATE_LAYOUT, "1998-Sep-04")
date3, _ := time.Parse(SHORT_DATE_LAYOUT, "2008-Apr-28")
orgs := []Organization{
{Id: OrganizationIds[0], Name: "Amazon", Location: "Seattle", DateFounded: date1},
{Id: OrganizationIds[1], Name: "Google", Location: "Mountain View", DateFounded: date2},
{Id: OrganizationIds[2], Name: "JFrog", Location: "Santa Clara", DateFounded: date3},
}
for _, org := range orgs {
err := session.DB(DB_NAME).C(ORGANIZATIONS_C).Insert(org)
if err != nil {
return err
}
}
return nil
}
func (r *MyMgoMethodsReceiver) V001_init_organizations_down(session *mgo.Session) error {
return session.DB(DB_NAME).C(ORGANIZATIONS_C).DropCollection()
}
func (r *MyMgoMethodsReceiver) V001_init_users_up(session *mgo.Session) error {
users := []User{
{Id: UserIds[0], Name: "Alex"},
{Id: UserIds[1], Name: "Beatrice"},
{Id: UserIds[2], Name: "Cleo"},
}
for _, user := range users {
err := session.DB(DB_NAME).C(USERS_C).Insert(user)
if err != nil {
return err
}
}
return nil
}
func (r *MyMgoMethodsReceiver) V001_init_users_down(session *mgo.Session) error {
return session.DB(DB_NAME).C(USERS_C).DropCollection()
}
func (r *MyMgoMethodsReceiver) V002_organizations_rename_location_field_to_headquarters_up(session *mgo.Session) error {
c := session.DB(DB_NAME).C(ORGANIZATIONS_C)
_, err := c.UpdateAll(nil, bson.M{"$rename": bson.M{"location": "headquarters"}})
return err
}
func (r *MyMgoMethodsReceiver) V002_organizations_rename_location_field_to_headquarters_down(session *mgo.Session) error {
c := session.DB(DB_NAME).C(ORGANIZATIONS_C)
_, err := c.UpdateAll(nil, bson.M{"$rename": bson.M{"headquarters": "location"}})
return err
}
func (r *MyMgoMethodsReceiver) V002_change_user_cleo_to_cleopatra_up(session *mgo.Session) error {
c := session.DB(DB_NAME).C(USERS_C)
colQuerier := bson.M{"name": "Cleo"}
change := bson.M{"$set": bson.M{"name": "Cleopatra"}}
return c.Update(colQuerier, change)
}
func (r *MyMgoMethodsReceiver) V002_change_user_cleo_to_cleopatra_down(session *mgo.Session) error {
c := session.DB(DB_NAME).C(USERS_C)
colQuerier := bson.M{"name": "Cleopatra"}
change := bson.M{"$set": bson.M{"name": "Cleo"}}
return c.Update(colQuerier, change)
}

View File

@ -1,4 +1,4 @@
package usage_examples
package mongodb_example
import (
"testing"
@ -6,13 +6,13 @@ import (
"github.com/dimag-jfrog/migrate/file"
"github.com/dimag-jfrog/migrate/migrate/direction"
"github.com/dimag-jfrog/migrate/driver/gomethods"
"github.com/dimag-jfrog/migrate/driver/gomethods/mongodb"
pipep "github.com/dimag-jfrog/migrate/pipe"
"reflect"
"time"
"github.com/dimag-jfrog/migrate/driver/gomethods"
)
type ExpectedMigrationResult struct {
Organizations []Organization
Organizations_v2 []Organization_v2
@ -23,7 +23,7 @@ type ExpectedMigrationResult struct {
func RunMigrationAndAssertResult(
t *testing.T,
title string,
d *GoMethodsMongoDbDriver,
d *mongodb.MongoDbGoMethodsDriver,
file file.File,
expected *ExpectedMigrationResult) {
@ -53,6 +53,10 @@ func RunMigrationAndAssertResult(
t.Fatal("Failed to query Users collection")
}
if !reflect.DeepEqual(expected.Errors, errs) {
t.Fatalf("Migration '%s': FAILED\nexpected errors %v\nbut got %v", title, expected.Errors, errs)
}
if !reflect.DeepEqual(expected.Organizations, actualOrganizations) {
t.Fatalf("Migration '%s': FAILED\nexpected organizations %v\nbut got %v", title, expected.Organizations, actualOrganizations)
}
@ -65,13 +69,9 @@ func RunMigrationAndAssertResult(
t.Fatalf("Migration '%s': FAILED\nexpected users %v\nbut got %v", title, expected.Users, actualUsers)
}
if !reflect.DeepEqual(expected.Errors, errs) {
t.Fatalf("Migration '%s': FAILED\nexpected errors %v\nbut got %v", title, expected.Errors, errs)
}
t.Logf("Migration '%s': PASSED", title)
}
func TestMigrate(t *testing.T) {
//host := os.Getenv("MONGODB_PORT_27017_TCP_ADDR")
//port := os.Getenv("MONGODB_PORT_27017_TCP_PORT")
@ -79,7 +79,9 @@ func TestMigrate(t *testing.T) {
port := "27017"
driverUrl := "mongodb://" + host + ":" + port
d := &GoMethodsMongoDbDriver{}
//gomethods.RegisterMethodsReceiver("MyMgoMethodsReceiver", &MyMgoMethodsReceiver{})
d := &mongodb.MongoDbGoMethodsDriver{}
if err := d.Initialize(driverUrl); err != nil {
t.Fatal(err)
}
@ -106,6 +108,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
MyMgoMethodsReceiver
V001_init_organizations_up
V001_init_users_up
`),
@ -134,6 +137,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
MyMgoMethodsReceiver
V002_organizations_rename_location_field_to_headquarters_up
V002_change_user_cleo_to_cleopatra_up
`),
@ -162,6 +166,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Down,
Content: []byte(`
MyMgoMethodsReceiver
V002_change_user_cleo_to_cleopatra_down
V002_organizations_rename_location_field_to_headquarters_down
`),
@ -190,6 +195,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Down,
Content: []byte(`
MyMgoMethodsReceiver
V001_init_users_down
V001_init_organizations_down
`),
@ -210,6 +216,7 @@ func TestMigrate(t *testing.T) {
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
MyMgoMethodsReceiver
V001_init_organizations_up
V001_init_users_up
v001_non_existing_method_up
@ -222,6 +229,26 @@ func TestMigrate(t *testing.T) {
Errors: []error{gomethods.MissingMethodError("v001_non_existing_method_up")},
},
},
{
name: "v0 -> v1: not defined message receiver",
file: file.File{
Path: "/foobar",
FileName: "001_foobar.up.gm",
Version: 1,
Name: "foobar",
Direction: direction.Up,
Content: []byte(`
V001_init_organizations_up
V001_init_users_up
`),
},
expectedResult: ExpectedMigrationResult{
Organizations: []Organization{},
Organizations_v2: []Organization_v2{},
Users: []User{},
Errors: []error{gomethods.UnregisteredMethodsReceiverError("V001_init_organizations_up")},
},
},
}
for _, m := range migrations {

View File

@ -1,140 +0,0 @@
package usage_examples
import (
"github.com/dimag-jfrog/migrate/driver"
"github.com/dimag-jfrog/migrate/driver/gomethods"
"github.com/dimag-jfrog/migrate/driver/gomethods/mongodb"
"gopkg.in/mgo.v2/bson"
"time"
)
// This boilerplate part is necessary and the same
// regardless of the specific mongodb golang methods driver
type GoMethodsMongoDbDriver struct {
mongodb.DriverTemplate
}
func (d *GoMethodsMongoDbDriver) Initialize(url string) error {
return d.DriverTemplate.Initialize(url, DB_NAME, gomethods.Migrator{MigrationMethodsReceiver: d})
}
func init() {
driver.RegisterDriver("mongodb", &GoMethodsMongoDbDriver{mongodb.DriverTemplate{}})
}
// Here goes the specific mongodb golang methods driver logic
const DB_NAME = "test"
const SHORT_DATE_LAYOUT = "2000-Jan-01"
const USERS_C = "users"
const ORGANIZATIONS_C = "organizations"
type Organization struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Name string `bson:"name"`
Location string `bson:"location"`
DateFounded time.Time `bson:"date_founded"`
}
type Organization_v2 struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Name string `bson:"name"`
Headquarters string `bson:"headquarters"`
DateFounded time.Time `bson:"date_founded"`
}
type User struct {
Id bson.ObjectId `bson:"_id"`
Name string `bson:"name"`
}
var OrganizationIds []bson.ObjectId = []bson.ObjectId{
bson.NewObjectId(),
bson.NewObjectId(),
bson.NewObjectId(),
}
var UserIds []bson.ObjectId = []bson.ObjectId{
bson.NewObjectId(),
bson.NewObjectId(),
bson.NewObjectId(),
}
func (m *GoMethodsMongoDbDriver) V001_init_organizations_up() error {
date1, _ := time.Parse(SHORT_DATE_LAYOUT, "1994-Jul-05")
date2, _ := time.Parse(SHORT_DATE_LAYOUT, "1998-Sep-04")
date3, _ := time.Parse(SHORT_DATE_LAYOUT, "2008-Apr-28")
orgs := []Organization{
{Id: OrganizationIds[0], Name: "Amazon", Location:"Seattle", DateFounded: date1},
{Id: OrganizationIds[1], Name: "Google", Location:"Mountain View", DateFounded: date2},
{Id: OrganizationIds[2], Name: "JFrog", Location:"Santa Clara", DateFounded: date3},
}
for _, org := range orgs {
err := m.Session.DB(DB_NAME).C(ORGANIZATIONS_C).Insert(org)
if err != nil {
return err
}
}
return nil
}
func (m *GoMethodsMongoDbDriver) V001_init_organizations_down() error {
return m.Session.DB(DB_NAME).C(ORGANIZATIONS_C).DropCollection()
}
func (m *GoMethodsMongoDbDriver) V001_init_users_up() error {
users := []User{
{Id: UserIds[0], Name: "Alex"},
{Id: UserIds[1], Name: "Beatrice"},
{Id: UserIds[2], Name: "Cleo"},
}
for _, user := range users {
err := m.Session.DB(DB_NAME).C(USERS_C).Insert(user)
if err != nil {
return err
}
}
return nil
}
func (m *GoMethodsMongoDbDriver) V001_init_users_down() error {
return m.Session.DB(DB_NAME).C(USERS_C).DropCollection()
}
func (m *GoMethodsMongoDbDriver) V002_organizations_rename_location_field_to_headquarters_up() error {
c := m.Session.DB(DB_NAME).C(ORGANIZATIONS_C)
_, err := c.UpdateAll(nil, bson.M{"$rename": bson.M{"location": "headquarters"}})
return err
}
func (m *GoMethodsMongoDbDriver) V002_organizations_rename_location_field_to_headquarters_down() error {
c := m.Session.DB(DB_NAME).C(ORGANIZATIONS_C)
_, err := c.UpdateAll(nil, bson.M{"$rename": bson.M{"headquarters": "location"}})
return err
}
func (m *GoMethodsMongoDbDriver) V002_change_user_cleo_to_cleopatra_up() error {
c := m.Session.DB(DB_NAME).C(USERS_C)
colQuerier := bson.M{"name": "Cleo"}
change := bson.M{"$set": bson.M{"name": "Cleopatra"}}
return c.Update(colQuerier, change)
}
func (m *GoMethodsMongoDbDriver) V002_change_user_cleo_to_cleopatra_down() error {
c := m.Session.DB(DB_NAME).C(USERS_C)
colQuerier := bson.M{"name": "Cleopatra"}
change := bson.M{"$set": bson.M{"name": "Cleo",}}
return c.Update(colQuerier, change)
}