package dbManager import ( "errors" "github.com/prometheus/client_golang/prometheus" "github.com/Cyclops-Labs/cyclops-4-hpc.git/services/plan-manager/models" l "gitlab.com/cyclops-utilities/logging" "gorm.io/driver/postgres" "gorm.io/gorm" ) const ( statusDuplicated = iota statusFail statusMissing statusOK ) // DbParameter is the struct defined to group and contain all the methods // that interact with the database. // Parameters: // - connStr: strings with the connection information to the database // - Db: a gorm.DB pointer to the db to invoke all the db methods type DbParameter struct { connStr string Db *gorm.DB Metrics map[string]*prometheus.GaugeVec } // New is the function to create the struct DbParameter. // Parameters: // - dbConn: strings with the connection information to the database // - tables: array of interfaces that will contains the models to migrate // to the database on initialization // Returns: // - DbParameter: struct to interact with dbManager functionalities func New(dbConn string, tables ...interface{}) *DbParameter { l.Trace.Printf("[DB] Gerenating new DBParameter.\n") var ( dp DbParameter err error ) dp.connStr = dbConn dp.Db, err = gorm.Open(postgres.Open(dbConn), &gorm.Config{}) if err != nil { l.Error.Printf("[DB] Error opening connection. Error: %v\n", err) } l.Trace.Printf("[DB] Migrating tables.\n") //Database migration, it handles everything dp.Db.AutoMigrate(tables...) l.Trace.Printf("[DB] Generating hypertables.\n") // Hypertables creation for timescaledb in case of needed //dp.Db.Exec("SELECT create_hypertable('" + dp.Db.NewScope(&models.TABLE).TableName() + "', 'TIMESCALE-ROW-INDEX');") return &dp } // CreateCycle function is to add a new cycle to the system. // Parameters: // - p: a reference to Cycle models containing the new data to be stored // in the system. // Returns: // - id: an int64 containing the id of the bundle just added to the db. // - status: an int for informing about the status of the operation. // - e: an error raised in case of problems with the operation. func (d *DbParameter) CreateCycle(p *models.Cycle) (id string, status int, e error) { l.Trace.Printf("[DB] Attempting to create a new Cycle now.\n") var p0 models.Cycle if r := d.Db.Where(p).First(&p0).Error; errors.Is(r, gorm.ErrRecordNotFound) { if r := d.Db.Create(p); r.Error == nil { l.Info.Printf("Inserted new record for cycle [ %v ] successfully.\n", *p.State) d.Metrics["count"].With(prometheus.Labels{"type": "Cycles added"}).Inc() status = statusOK id = (*r.Statement.Model.(*models.Cycle)).ID } else { l.Warning.Printf("Unable to insert the record for cycle [ %v ], check with administrator.\n", *p.State) e = r.Error status = statusFail } } else { l.Warning.Printf("Record for cycle [ %v ] already exists, check with administrator.\n", *p.State) status = statusDuplicated } return } // CreatePlan function is to add a new plan to the system. // Parameters: // - p: a reference to Plan models containing the new data to be stored // in the system. // Returns: // - id: a string containing the id of the bundle just added to the db. // - status: an int for informing about the status of the operation. // - e: an error raised in case of problems with the operation. func (d *DbParameter) CreatePlan(p *models.Plan) (id string, status int, e error) { l.Trace.Printf("[DB] Attempting to create a new Plan now.\n") var p0 models.Plan if r := d.Db.Where(p).First(&p0).Error; errors.Is(r, gorm.ErrRecordNotFound) { if r := d.Db.Create(p); r.Error == nil { l.Info.Printf("Inserted new record for plan [ %v ] successfully.\n", *p.Name) d.Metrics["count"].With(prometheus.Labels{"type": "Plans added"}).Inc() status = statusOK id = (*r.Statement.Model.(*models.Plan)).ID } else { l.Warning.Printf("Unable to insert the record for plan [ %v ], check with administrator.\n", *p.Name) e = r.Error status = statusFail } } else { l.Warning.Printf("Record for plan [ %v ] already exists, check with administrator.\n", *p.Name) status = statusDuplicated } return } // CreateSku function is to add a new sku to the system. // Parameters: // - s: a reference to Sku models containing the new data to be stored // in the system. // Returns: // - id: an int64 containing the id of the bundle just added to the db. // - status: an int for informing about the status of the operation. // - e: an error raised in case of problems with the operation. func (d *DbParameter) CreateSku(s *models.Sku) (id string, status int, e error) { l.Trace.Printf("[DB] Attempting to create a new Sku now.\n") var s0 models.Sku if r := d.Db.Where(s).First(&s0).Error; errors.Is(r, gorm.ErrRecordNotFound) { if r := d.Db.Create(s); r.Error == nil { l.Info.Printf("Inserted new record for sku [ %v ] successfully.\n", *s.Name) d.Metrics["count"].With(prometheus.Labels{"type": "Skus added"}).Inc() status = statusOK id = (*r.Statement.Model.(*models.Sku)).ID } else { l.Warning.Printf("Unable to insert the record for sku [ %v ], check with administrator.\n", *s.Name) e = r.Error status = statusFail } } else { l.Warning.Printf("Record for sku [ %v ] already exists, check with administrator.\n", *s.Name) status = statusDuplicated } return } // CreateSkuBundle function is to add a new bundle to the system. // Parameters: // - f: a reference to Bundle models containing the new data to be stored // in the system. // Returns: // - id: an int64 containing the id of the bundle just added to the db. // - status: an int for informing about the status of the operation. // - e: an error raised in case of problems with the operation. func (d *DbParameter) CreateSkuBundle(f *models.SkuBundle) (id string, status int, e error) { l.Trace.Printf("[DB] Attempting to create a new Bundle now.\n") var f0 models.SkuBundle if r := d.Db.Where(f).First(&f0).Error; errors.Is(r, gorm.ErrRecordNotFound) { if r := d.Db.Create(f); r.Error == nil { l.Info.Printf("Inserted new record for bundle [ %v ] successfully.\n", *f.Name) d.Metrics["count"].With(prometheus.Labels{"type": "Bundles added"}).Inc() status = statusOK id = (r.Statement.Model.(*models.SkuBundle)).ID } else { l.Warning.Printf("Unable to insert the record for bundle [ %v ], check with administrator.\n", *f.Name) e = r.Error status = statusFail } } else { l.Warning.Printf("Record for bundle [ %v ] already exists, check with administrator. Error: %v\n", *f.Name, r) id = f0.ID status = statusDuplicated } return } // CreateSkuPrice function is to add a new sku price to the system. // Parameters: // - sp: a reference to Sku Price models containing the new data to be stored // in the system. // Returns: // - id: an int64 containing the id of the bundle just added to the db. // - status: an int for informing about the status of the operation. // - e: an error raised in case of problems with the operation. func (d *DbParameter) CreateSkuPrice(sp *models.SkuPrice) (id string, status int, e error) { l.Trace.Printf("[DB] Attempting to create a new Sku Price now.\n") var sp0 models.SkuPrice if r := d.Db.Where(sp).First(&sp0).Error; errors.Is(r, gorm.ErrRecordNotFound) { if r := d.Db.Create(sp); r.Error == nil { l.Info.Printf("Inserted new record for sku price [ %v ] successfully.\n", sp.SkuName) d.Metrics["count"].With(prometheus.Labels{"type": "Sku Prices added"}).Inc() status = statusOK id = (*r.Statement.Model.(*models.SkuPrice)).ID } else { l.Warning.Printf("Unable to insert the record for sku price [ %v ], check with administrator.\n", sp.SkuName) e = r.Error status = statusFail } } else { l.Warning.Printf("Record for sku price [ %v ] already exists, check with administrator.\n", sp.SkuName) status = statusDuplicated } return } // GetCompletePlan function is to retrieve a completed plan (includings its // linked skus) from the system provided its id. // Parameters: // - id: a string containing the id of the plan to be retrieved from the system. // Returns: // - reference to Plan model containing the requested one stored in the system. // - error raised in case of problems with the operation. func (d *DbParameter) GetCompletePlan(id string) (*models.Plan, error) { l.Trace.Printf("[DB] Attempting to retrieve the Complete Plan [ %v ] now.\n", id) var object models.Plan var e error if e = d.Db.Where(&models.Plan{ID: id}).First(&object).Error; errors.Is(e, gorm.ErrRecordNotFound) { l.Trace.Printf("[DB] Complete plan with id: %v doesn't exist in the system, check with administrator.", id) } d.Db.Where(&models.SkuPrice{PlanID: &object.ID}).Find(&object.SkuPrices) return &object, e } // GetCycle function is to retrieve a cycle from the system provided its id. // Parameters: // - id: an int64 containing the id of the cycle to be retrieved from the system. // Returns: // - reference to Cycle model containing the requested one stored in the system. // - error raised in case of problems with the operation. func (d *DbParameter) GetCycle(id string) (*models.Cycle, error) { l.Trace.Printf("[DB] Attempting to retrieve the Cycle [ %v ] now.\n", id) var object models.Cycle var e error if e = d.Db.Where(&models.Cycle{ID: id}).First(&object).Error; errors.Is(e, gorm.ErrRecordNotFound) { l.Trace.Printf("[DB] Cycle with id: %v doesn't exist in the system, check with administrator.", id) } return &object, e } // GetPlan function is to retrieve a plan from the system provided its id. // Parameters: // - id: a string containing the id of the plan to be retrieved from the system. // Returns: // - reference to Plan model containing the requested one stored in the system. // - error raised in case of problems with the operation. func (d *DbParameter) GetPlan(id string) (*models.Plan, error) { l.Trace.Printf("[DB] Attempting to retrieve the Plan [ %v ] now.\n", id) var object models.Plan var e error if e = d.Db.Where(&models.Plan{ID: id}).First(&object).Error; errors.Is(e, gorm.ErrRecordNotFound) { l.Trace.Printf("[DB] Plan with id: %v doesn't exist in the system, check with administrator.", id) } return &object, e } // GetSku function is to retrieve a sku from the system provided its id. // Parameters: // - id: an int64 containing the id of the sku to be retrieved from the system. // Returns: // - reference to Sku model containing the requested one stored in the system. // - error raised in case of problems with the operation. func (d *DbParameter) GetSku(id string) (*models.Sku, error) { l.Trace.Printf("[DB] Attempting to retrieve the Sku [ %v ] now.\n", id) var object models.Sku var e error if e = d.Db.Where(&models.Sku{ID: id}).First(&object).Error; errors.Is(e, gorm.ErrRecordNotFound) { l.Trace.Printf("[DB] Sku with id: %v doesn't exist in the system, check with administrator.", id) } return &object, e } // GetSkuBundle function is to retrieve a bundle from the system provided its id. // Parameters: // - id: an int64 containing the id of the bundle to be retrieved from the system. // Returns: // - reference to Bundle model containing the requested one stored in the system. // - error raised in case of problems with the operation. func (d *DbParameter) GetSkuBundle(id string) (*models.SkuBundle, error) { l.Trace.Printf("[DB] Attempting to retrieve the Bundle [ %v ] now.\n", id) var object models.SkuBundle var e error if e = d.Db.Where(&models.SkuBundle{ID: id}).First(&object).Error; errors.Is(e, gorm.ErrRecordNotFound) { l.Trace.Printf("[DB] Bundle with id: %v doesn't exist in the system, check with administrator.", id) } return &object, e } // GetSkuBundleByName function is to retrieve a bundle from the system provided its // name. // Parameters: // - name: a string containing the name of the bundle to be retrieved from the system. // Returns: // - reference to Bundle model containing the requested one stored in the system. // - error raised in case of problems with the operation. func (d *DbParameter) GetSkuBundleByName(name string) (*models.SkuBundle, error) { l.Trace.Printf("[DB] Attempting to retrieve the Bundle [ %v ] now.\n", name) var object models.SkuBundle var e error if e = d.Db.Where(&models.SkuBundle{Name: &name}).First(&object).Error; errors.Is(e, gorm.ErrRecordNotFound) { l.Trace.Printf("[DB] Bundle with name: %v doesn't exist in the system, check with administrator.", name) } return &object, e } // GetSkuPrice function is to retrieve a sku price from the system provided its id. // Parameters: // - id: an int64 containing the id of the sku price to be retrieved from the system. // Returns: // - reference to Sku Price model containing the requested one stored in the system. // - error raised in case of problems with the operation. func (d *DbParameter) GetSkuPrice(id string) (*models.SkuPrice, error) { l.Trace.Printf("[DB] Attempting to retrieve the Sku Price [ %v ] now.\n", id) var object models.SkuPrice var e error if e = d.Db.Where(&models.SkuPrice{ID: id}).First(&object).Error; errors.Is(e, gorm.ErrRecordNotFound) { l.Trace.Printf("[DB] Sku price with id: %v doesn't exist in the system, check with administrator.", id) } return &object, e } // ListCompletePlans function is to retrieve all the complete plans (with the // linked skus) contained in the system. // Returns: // - p: a reference to Plan models containing the list of them stored in the system. // - e: an error raised in case of problems with the operation. func (d *DbParameter) ListCompletePlans() (p []*models.Plan, e error) { l.Trace.Printf("[DB] Attempting to retrieve all the Complete Plans in the system now.\n") var p0 []*models.Plan if e := d.Db.Find(&p0).Error; e != nil { l.Warning.Printf("[DB] Error in DB operation. Error: %v\n", e) } for i := range p0 { plan := p0[i] d.Db.Where(&models.SkuPrice{PlanID: &p0[i].ID}).Find(&plan.SkuPrices) p = append(p, plan) } l.Trace.Printf("[DB] Found [ %d ] complete plans in the db.\n", len(p)) return } // ListCycles function is to retrieve all the cycles contained in the system. // Returns: // - p: a reference to Cycle models containing the list of them stored in the system. // - e: an error raised in case of problems with the operation. func (d *DbParameter) ListCycles(state, ty string) (p []*models.Cycle, e error) { l.Trace.Printf("[DB] Attempting to retrieve all the Cycles in the system now.\n") var c models.Cycle if state != "" { c.State = &state } if ty != "" { c.ResourceType = &ty } if e := d.Db.Where(&c).Find(&p).Error; e != nil { l.Warning.Printf("[DB] Error in DB operation. Error: %v\n", e) } l.Trace.Printf("[DB] Found [ %d ] cycles in the db.\n", len(p)) return } // ListPlans function is to retrieve all the plans contained in the system. // Returns: // - p: a reference to Plan models containing the list of them stored in the system. // - e: an error raised in case of problems with the operation. func (d *DbParameter) ListPlans() (p []*models.Plan, e error) { l.Trace.Printf("[DB] Attempting to retrieve all the Plans in the system now.\n") if e := d.Db.Find(&p).Error; e != nil { l.Warning.Printf("[DB] Error in DB operation. Error: %v\n", e) } l.Trace.Printf("[DB] Found [ %d ] plans in the db.\n", len(p)) return } // ListSkuBundles function is to retrieve all the bundles contained in the system. // Returns: // - f: a reference to Bundle models containing the list of them stored in the system. // - e: an error raised in case of problems with the operation. func (d *DbParameter) ListSkuBundles() (f []*models.SkuBundle, e error) { l.Trace.Printf("[DB] Attempting to retrieve all the Bundles in the system now.\n") if e := d.Db.Find(&f).Error; e != nil { l.Warning.Printf("[DB] Error in DB operation. Error: %v\n", e) } l.Trace.Printf("[DB] Found [ %d ] bundles in the db.\n", len(f)) return } // ListSkuPrices function is to retrieve all the sku prices contained in the system. // Returns: // - sp: a reference to Sku Price models containing the list of them stored in the system. // - e: an error raised in case of problems with the operation. func (d *DbParameter) ListSkuPrices() (sp []*models.SkuPrice, e error) { l.Trace.Printf("[DB] Attempting to retrieve all the Sku Prices in the system now.\n") if e := d.Db.Find(&sp).Error; e != nil { l.Warning.Printf("[DB] Error in DB operation. Error: %v\n", e) } l.Trace.Printf("[DB] Found [ %d ] sku prices in the db.\n", len(sp)) return } // ListSkus function is to retrieve all the skus contained in the system. // Returns: // - s: a reference to Sku models containing the list of them stored in the system. // - e: an error raised in case of problems with the operation. func (d *DbParameter) ListSkus() (s []*models.Sku, e error) { l.Trace.Printf("[DB] Attempting to retrieve all the Skus in the system now.\n") if e := d.Db.Find(&s).Error; e != nil { l.Warning.Printf("[DB] Error in DB operation. Error: %v\n", e) } l.Trace.Printf("[DB] Found [ %d ] skus in the db.\n", len(s)) return } // UpdateCycle function is to update the cycle linked to the provided ID // with the provided new data. // Parameters: // - id: an int64 containing the id of the sku price to be updated in the db. // - p: a reference to Cycle models containing the new data to be updated. // Returns: // - id: an int64 containing the id of the bundle just added to the db. // - status: an int for informing about the status of the operation. // - e: an error raised in case of problems with the operation. func (d *DbParameter) UpdateCycle(id string, p *models.Cycle) (status int, e error) { l.Trace.Printf("[DB] Attempting to update the Cycle [ %v ] now.\n", id) var p0 models.Cycle if r := d.Db.Where(&models.Cycle{ID: id}).First(&p0).Error; !errors.Is(r, gorm.ErrRecordNotFound) { if e := d.Db.Model(&p0).Updates(p).Error; e == nil { l.Info.Printf("[DB] Updated record for cycle [ %v ] successfully.\n", p.ID) d.Metrics["count"].With(prometheus.Labels{"type": "Cycles updated"}).Inc() status = statusOK } else { l.Warning.Printf("[DB] Unable to update record for cycle [ %v ], check with administrator.\n", p.ID) status = statusFail } } else { l.Warning.Printf("[DB] Record for cycle [ %v ] not found, check with administrator.\n", p.ID) status = statusMissing } return } // UpdatePlan function is to update the plan linked to the provided ID // with the provided new data. // Parameters: // - id: a string containing the id of the sku price to be updated in the db. // - p: a reference to Plan models containing the new data to be updated. // Returns: // - id: an int64 containing the id of the bundle just added to the db. // - status: an int for informing about the status of the operation. // - e: an error raised in case of problems with the operation. func (d *DbParameter) UpdatePlan(id string, p *models.Plan) (status int, e error) { l.Trace.Printf("[DB] Attempting to update the Plan [ %v ] now.\n", id) var p0 models.Plan if r := d.Db.Where(&models.Plan{ID: id}).First(&p0).Error; !errors.Is(r, gorm.ErrRecordNotFound) { if e := d.Db.Model(&p0).Updates(p).Error; e == nil { l.Info.Printf("[DB] Updated record for plan [ %v ] successfully.\n", p.ID) d.Metrics["count"].With(prometheus.Labels{"type": "Plans updated"}).Inc() status = statusOK } else { l.Warning.Printf("[DB] Unable to update record for plan [ %v ], check with administrator.\n", p.ID) status = statusFail } } else { l.Warning.Printf("[DB] Record for plan [ %v ] not found, check with administrator.\n", p.ID) status = statusMissing } return } // UpdateSku function is to update the sku linked to the provided ID // with the provided new data. // Parameters: // - id: an int64 containing the id of the sku price to be updated in the db. // - s: a reference to Sku models containing the new data to be updated. // Returns: // - id: an int64 containing the id of the bundle just added to the db. // - status: an int for informing about the status of the operation. // - e: an error raised in case of problems with the operation. func (d *DbParameter) UpdateSku(id string, s *models.Sku) (status int, e error) { l.Trace.Printf("[DB] Attempting to update the Sku [ %v ] now.\n", id) var s0 models.Sku if r := d.Db.Where(&models.Sku{ID: id}).First(&s0).Error; !errors.Is(r, gorm.ErrRecordNotFound) { if e := d.Db.Model(&s0).Updates(s).Error; e == nil { l.Info.Printf("[DB] Updated record for sku [ %v ] successfully.\n", s.ID) d.Metrics["count"].With(prometheus.Labels{"type": "Skus updated"}).Inc() status = statusOK } else { l.Warning.Printf("[DB] Unable to update record for sku [ %v ], check with administrator.\n", s.ID) status = statusFail } } else { l.Warning.Printf("[DB] Record for sku [ %v ] not found, check with administrator.\n", s.ID) status = statusMissing } return } // UpdateSkuBundle function is to update the bundle linked to the provided ID // with the provided new data. // Parameters: // - f: a reference to Bundle models containing the new data to be updated. // Returns: // - id: an int64 containing the id of the bundle just added to the db. // - status: an int for informing about the status of the operation. // - e: an error raised in case of problems with the operation. func (d *DbParameter) UpdateSkuBundle(id string, b *models.SkuBundle) (status int, e error) { l.Trace.Printf("[DB] Attempting to update the Bundle [ %v ] now.\n", b.ID) var b0 models.SkuBundle if r := d.Db.Where(&models.SkuBundle{ID: id}).First(&b0).Error; !errors.Is(r, gorm.ErrRecordNotFound) { if e := d.Db.Model(&b0).Updates(b).Error; e == nil { l.Info.Printf("[DB] Updated record for bundle [ %v ] successfully.\n", b.ID) d.Metrics["count"].With(prometheus.Labels{"type": "Bundles updated"}).Inc() status = statusOK } else { l.Warning.Printf("[DB] Unable to update record for bundle [ %v ], check with administrator.\n", b.ID) status = statusFail } } else { l.Warning.Printf("[DB] Record for bundle [ %v ] not found, check with administrator.\n", b.ID) status = statusMissing } return } // UpdateSkuPrice function is to update the sku price linked to the provided ID // with the provided new data. // Parameters: // - id: an int64 containing the id of the sku price to be updated in the db. // - sp: a reference to SkuPrice models containing the new data to be updated. // Returns: // - status: an int for informing about the status of the operation. // - e: an error raised in case of problems with the operation. func (d *DbParameter) UpdateSkuPrice(id string, sp *models.SkuPrice) (status int, e error) { l.Trace.Printf("[DB] Attempting to update the Sku Price [ %v ] now.\n", id) var sp0 models.SkuPrice if r := d.Db.Where(&models.SkuPrice{ID: id}).First(&sp0).Error; !errors.Is(r, gorm.ErrRecordNotFound) { if e := d.Db.Model(&sp0).Updates(sp).Error; e == nil { l.Info.Printf("[DB] Updated record for sku price [ %v ] successfully.\n", sp.ID) d.Metrics["count"].With(prometheus.Labels{"type": "Sku Prices updated"}).Inc() status = statusOK } else { l.Warning.Printf("[DB] Unable to update record for sku price [ %v ], check with administrator.\n", sp.ID) status = statusFail } } else { l.Warning.Printf("[DB] Record for sku price [ %v ] not found, check with administrator.\n", sp.ID) status = statusMissing } return }