github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/utils/migration/migration.go (about)

     1  package migration
     2  
     3  import (
     4  	"crypto/sha256"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/unicornultrafoundation/go-u2u/log"
     9  )
    10  
    11  // Migration is a migration step.
    12  type Migration struct {
    13  	name string
    14  	exec func() error
    15  	prev *Migration
    16  }
    17  
    18  // Begin with empty unique migration step.
    19  func Begin(appName string) *Migration {
    20  	return &Migration{
    21  		name: appName,
    22  	}
    23  }
    24  
    25  // Next creates next migration.
    26  func (m *Migration) Next(name string, exec func() error) *Migration {
    27  	if name == "" {
    28  		panic("empty name")
    29  	}
    30  
    31  	if exec == nil {
    32  		panic("empty exec")
    33  	}
    34  
    35  	return &Migration{
    36  		name: name,
    37  		exec: exec,
    38  		prev: m,
    39  	}
    40  }
    41  
    42  func idOf(name string) string {
    43  	digest := sha256.New()
    44  	digest.Write([]byte(name))
    45  
    46  	bytes := digest.Sum(nil)
    47  	return fmt.Sprintf("%x", bytes)
    48  }
    49  
    50  // ID is an uniq migration's id.
    51  func (m *Migration) ID() string {
    52  	return idOf(m.name)
    53  }
    54  
    55  // Exec method run migrations chain in order
    56  func (m *Migration) Exec(curr IDStore, flush func() error) error {
    57  	currID := curr.GetID()
    58  	myID := m.ID()
    59  
    60  	if m.veryFirst() {
    61  		if currID != myID {
    62  			return errors.New("unknown version: " + currID)
    63  		}
    64  		return nil
    65  	}
    66  
    67  	if currID == myID {
    68  		return nil
    69  	}
    70  
    71  	err := m.prev.Exec(curr, flush)
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	log.Warn("Applying migration", "name", m.name)
    77  	err = m.exec()
    78  	if err != nil {
    79  		log.Error("'"+m.name+"' migration failed", "err", err)
    80  		return err
    81  	}
    82  
    83  	curr.SetID(myID)
    84  
    85  	return flush()
    86  }
    87  
    88  func (m *Migration) veryFirst() bool {
    89  	return m.exec == nil
    90  }
    91  
    92  // IDs return list of migrations ids in chain
    93  func (m *Migration) IDs() []string {
    94  	if m.prev == nil {
    95  		return []string{m.ID()}
    96  	}
    97  
    98  	return append(m.prev.IDs(), m.ID())
    99  }