github.com/abolfazlbeh/zhycan@v0.0.0-20230819144214-24cf38237387/internal/db/manager.go (about)

     1  package db
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/abolfazlbeh/zhycan/internal/config"
     6  	"github.com/abolfazlbeh/zhycan/internal/utils"
     7  	"go.mongodb.org/mongo-driver/mongo"
     8  	"gorm.io/gorm"
     9  	"log"
    10  	"reflect"
    11  	"strings"
    12  	"sync"
    13  )
    14  
    15  // Mark: manager
    16  
    17  // manager object
    18  type manager struct {
    19  	name                string
    20  	lock                sync.Mutex
    21  	sqliteDbInstances   map[string]*SqlWrapper[Sqlite]
    22  	mysqlDbInstances    map[string]*SqlWrapper[Mysql]
    23  	postgresDbInstances map[string]*SqlWrapper[Postgresql]
    24  	mongoDbInstances    map[string]*MongoWrapper
    25  	supportedDBs        []string
    26  
    27  	isManagerInitialized bool
    28  }
    29  
    30  // MARK: Module variables
    31  var managerInstance *manager = nil
    32  var once sync.Once
    33  
    34  // Module init function
    35  func init() {
    36  	log.Println("DB Manager Package Initialized...")
    37  }
    38  
    39  // init - Manager Constructor - It initializes the manager configuration params
    40  func (m *manager) init() {
    41  	m.name = "db"
    42  	m.isManagerInitialized = false
    43  
    44  	m.lock.Lock()
    45  	defer m.lock.Unlock()
    46  
    47  	m.supportedDBs = []string{"sqlite", "mysql", "postgresql", "mongodb"}
    48  
    49  	// read configs
    50  	connectionsObj, err := config.GetManager().Get(m.name, "connections")
    51  	if err != nil {
    52  		return
    53  	}
    54  
    55  	m.sqliteDbInstances = make(map[string]*SqlWrapper[Sqlite])
    56  	m.mysqlDbInstances = make(map[string]*SqlWrapper[Mysql])
    57  	m.postgresDbInstances = make(map[string]*SqlWrapper[Postgresql])
    58  	m.mongoDbInstances = make(map[string]*MongoWrapper)
    59  
    60  	for _, item := range connectionsObj.([]interface{}) {
    61  		dbInstanceName := item.(string)
    62  
    63  		dbTypeKey := fmt.Sprintf("%s.%s", dbInstanceName, "type")
    64  		dbTypeInf, err := config.GetManager().Get(m.name, dbTypeKey)
    65  		if err != nil {
    66  			continue
    67  		}
    68  
    69  		//  create a new instance based on type
    70  		dbType := strings.ToLower(dbTypeInf.(string))
    71  		if utils.ArrayContains(&m.supportedDBs, dbType) {
    72  			switch dbType {
    73  			case "sqlite":
    74  				obj, err := NewSqlWrapper[Sqlite](fmt.Sprintf("db/%s", dbInstanceName), dbType)
    75  				if err != nil {
    76  					// TODO: log error here
    77  					continue
    78  				}
    79  
    80  				m.sqliteDbInstances[dbInstanceName] = reflect.ValueOf(obj).Interface().(*SqlWrapper[Sqlite])
    81  				break
    82  			case "mysql":
    83  				obj, err := NewSqlWrapper[Mysql](fmt.Sprintf("db/%s", dbInstanceName), dbType)
    84  				if err != nil {
    85  					// TODO: log error here
    86  					continue
    87  				}
    88  
    89  				m.mysqlDbInstances[dbInstanceName] = reflect.ValueOf(obj).Interface().(*SqlWrapper[Mysql])
    90  				break
    91  			case "postgresql":
    92  				obj, err := NewSqlWrapper[Postgresql](fmt.Sprintf("db/%s", dbInstanceName), dbType)
    93  				if err != nil {
    94  					// TODO: log error here
    95  					continue
    96  				}
    97  
    98  				m.postgresDbInstances[dbInstanceName] = reflect.ValueOf(obj).Interface().(*SqlWrapper[Postgresql])
    99  				break
   100  			case "mongodb":
   101  				obj, err := NewMongoWrapper(fmt.Sprintf("db/%s", dbInstanceName))
   102  				if err != nil {
   103  					// TODO: log error here
   104  					continue
   105  				}
   106  				m.mongoDbInstances[dbInstanceName] = obj
   107  			}
   108  		}
   109  	}
   110  
   111  	m.isManagerInitialized = true
   112  }
   113  
   114  // restartOnChangeConfig - subscribe a function for when the config is changed
   115  func (m *manager) restartOnChangeConfig() {
   116  	// Config config server to reload
   117  	wrapper, err := config.GetManager().GetConfigWrapper(m.name)
   118  	if err == nil {
   119  		wrapper.RegisterChangeCallback(func() interface{} {
   120  			if m.isManagerInitialized {
   121  				m.init()
   122  			}
   123  			return nil
   124  		})
   125  	} else {
   126  		// TODO: make some logs
   127  	}
   128  }
   129  
   130  // MARK: Public Functions
   131  
   132  // GetManager - This function returns singleton instance of Db Manager
   133  func GetManager() *manager {
   134  	// once used for prevent race condition and manage critical section.
   135  	once.Do(func() {
   136  		managerInstance = &manager{}
   137  		managerInstance.init()
   138  		managerInstance.restartOnChangeConfig()
   139  	})
   140  	return managerInstance
   141  }
   142  
   143  // GetDb - Get *gorm.DB instance from the underlying interfaces
   144  func (m *manager) GetDb(instanceName string) (*gorm.DB, error) {
   145  	if m.isManagerInitialized {
   146  		if v, ok := m.sqliteDbInstances[instanceName]; ok {
   147  			return v.GetDb()
   148  		} else if v, ok := m.mysqlDbInstances[instanceName]; ok {
   149  			return v.GetDb()
   150  		} else if v, ok := m.postgresDbInstances[instanceName]; ok {
   151  			return v.GetDb()
   152  		}
   153  	}
   154  	return nil, NewNotExistServiceNameErr(instanceName)
   155  }
   156  
   157  // GetMongoDb - Get *mongo.Client instance from the underlying interfaces
   158  func (m *manager) GetMongoDb(instanceName string) (*mongo.Client, error) {
   159  	if m.isManagerInitialized {
   160  		if v, ok := m.mongoDbInstances[instanceName]; ok {
   161  			return v.GetDb()
   162  		}
   163  	}
   164  	return nil, NewNotExistServiceNameErr(instanceName)
   165  }
   166  
   167  // Migrate - migrate models on specific database
   168  func (m *manager) Migrate(instanceName string, models ...interface{}) error {
   169  	if m.isManagerInitialized {
   170  		if v, ok := m.sqliteDbInstances[instanceName]; ok {
   171  			return v.Migrate(models)
   172  		} else if v, ok := m.mysqlDbInstances[instanceName]; ok {
   173  			return v.Migrate(models)
   174  		} else if v, ok := m.postgresDbInstances[instanceName]; ok {
   175  			return v.Migrate(models)
   176  		}
   177  	}
   178  	return NewNotExistServiceNameErr(instanceName)
   179  }
   180  
   181  // AttachMigrationFunc -  attach migration function to be called by end user
   182  func (m *manager) AttachMigrationFunc(instanceName string, f func(migrator gorm.Migrator) error) error {
   183  	if m.isManagerInitialized {
   184  		if v, ok := m.sqliteDbInstances[instanceName]; ok {
   185  			return v.AttachMigrationFunc(f)
   186  		} else if v, ok := m.mysqlDbInstances[instanceName]; ok {
   187  			return v.AttachMigrationFunc(f)
   188  		} else if v, ok := m.postgresDbInstances[instanceName]; ok {
   189  			return v.AttachMigrationFunc(f)
   190  		}
   191  	}
   192  	return NewNotExistServiceNameErr(instanceName)
   193  }