...

Source file src/github.com/ory/x/popx/migration_box.go

Documentation: github.com/ory/x/popx

     1  package popx
     2  
     3  import (
     4  	"embed"
     5  	"io/fs"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/gobuffalo/pop/v5"
    10  	"github.com/pkg/errors"
    11  
    12  	"github.com/ory/x/logrusx"
    13  )
    14  
    15  type (
    16  	// MigrationBox is a embed migration box.
    17  	MigrationBox struct {
    18  		*Migrator
    19  
    20  		Dir              embed.FS
    21  		l                *logrusx.Logger
    22  		migrationContent MigrationContent
    23  	}
    24  	MigrationContent func(mf Migration, c *pop.Connection, r []byte, usingTemplate bool) (string, error)
    25  )
    26  
    27  func WithTemplateValues(v map[string]interface{}) func(*MigrationBox) *MigrationBox {
    28  	return func(m *MigrationBox) *MigrationBox {
    29  		m.migrationContent = ParameterizedMigrationContent(v)
    30  		return m
    31  	}
    32  }
    33  
    34  func WithMigrationContentMiddleware(middleware func(content string, err error) (string, error)) func(*MigrationBox) *MigrationBox {
    35  	return func(m *MigrationBox) *MigrationBox {
    36  		prev := m.migrationContent
    37  		m.migrationContent = func(mf Migration, c *pop.Connection, r []byte, usingTemplate bool) (string, error) {
    38  			return middleware(prev(mf, c, r, usingTemplate))
    39  		}
    40  		return m
    41  	}
    42  }
    43  
    44  // NewMigrationBox from a packr.Dir and a Connection.
    45  //
    46  //	migrations, err := NewMigrationBox(pkger.Dir("/migrations"))
    47  //
    48  func NewMigrationBox(dir embed.FS, m *Migrator, opts ...func(*MigrationBox) *MigrationBox) (*MigrationBox, error) {
    49  	mb := &MigrationBox{
    50  		Migrator:         m,
    51  		Dir:              dir,
    52  		l:                m.l,
    53  		migrationContent: ParameterizedMigrationContent(nil),
    54  	}
    55  
    56  	for _, o := range opts {
    57  		mb = o(mb)
    58  	}
    59  
    60  	runner := func(b []byte) func(Migration, *pop.Connection, *pop.Tx) error {
    61  		return func(mf Migration, c *pop.Connection, tx *pop.Tx) error {
    62  			content, err := mb.migrationContent(mf, c, b, true)
    63  			if err != nil {
    64  				return errors.Wrapf(err, "error processing %s", mf.Path)
    65  			}
    66  			if content == "" {
    67  				m.l.WithField("migration", mf.Path).Warn("Ignoring migration because content is empty.")
    68  				return nil
    69  			}
    70  			if _, err = tx.Exec(content); err != nil {
    71  				return errors.Wrapf(err, "error executing %s, sql: %s", mf.Path, content)
    72  			}
    73  			return nil
    74  		}
    75  	}
    76  
    77  	err := mb.findMigrations(runner)
    78  	if err != nil {
    79  		return mb, err
    80  	}
    81  
    82  	return mb, nil
    83  }
    84  
    85  func (fm *MigrationBox) findMigrations(runner func([]byte) func(mf Migration, c *pop.Connection, tx *pop.Tx) error) error {
    86  	return fs.WalkDir(fm.Dir, ".", func(p string, info fs.DirEntry, err error) error {
    87  		if err != nil {
    88  			return errors.WithStack(err)
    89  		}
    90  
    91  		if info.IsDir() {
    92  			return nil
    93  		}
    94  
    95  		match, err := pop.ParseMigrationFilename(info.Name())
    96  		if err != nil {
    97  			if strings.HasPrefix(err.Error(), "unsupported dialect") {
    98  				fm.l.Debugf("Ignoring migration file %s because dialect is not supported: %s", info.Name(), err.Error())
    99  				return nil
   100  			}
   101  			return errors.WithStack(err)
   102  		}
   103  
   104  		if match == nil {
   105  			fm.l.Debugf("Ignoring migration file %s because it does not match the file pattern.", info.Name())
   106  			return nil
   107  		}
   108  
   109  		content, err := fm.Dir.ReadFile(p)
   110  		if err != nil {
   111  			return errors.WithStack(err)
   112  		}
   113  
   114  		mf := Migration{
   115  			Path:      p,
   116  			Version:   match.Version,
   117  			Name:      match.Name,
   118  			DBType:    match.DBType,
   119  			Direction: match.Direction,
   120  			Type:      match.Type,
   121  			Runner:    runner(content),
   122  		}
   123  		fm.Migrations[mf.Direction] = append(fm.Migrations[mf.Direction], mf)
   124  		mod := sortIdent(fm.Migrations[mf.Direction])
   125  		if mf.Direction == "down" {
   126  			mod = sort.Reverse(mod)
   127  		}
   128  		sort.Sort(mod)
   129  		return nil
   130  	})
   131  }
   132  

View as plain text