Initial cloud-services repo - gateway service + pkg modules

This commit is contained in:
Chris Rai
2026-01-30 23:14:52 -05:00
commit fbb820d7b3
1037 changed files with 171318 additions and 0 deletions

View File

@@ -0,0 +1,426 @@
package queries
import (
"fmt"
"fiskerinc.com/modules/common"
"fiskerinc.com/modules/common/carupdatestatus"
"fiskerinc.com/modules/logger"
"fiskerinc.com/modules/validator"
"github.com/go-pg/pg/v10"
"github.com/go-pg/pg/v10/orm"
"github.com/pkg/errors"
)
var FINAL_UPDATE_STATUS = carupdatestatus.FINAL_UPDATE_STATUS
type CarUpdatesInterface interface {
Count(filter *common.CarUpdate) (int, error)
Delete(update *common.CarUpdate) (orm.Result, error)
CountUpdateStatuses(carupdateid int64) (int, error)
GetUpdateStatuses(carupdateid int64, paging *PageQueryOptions) ([]common.CarUpdateStatus, error)
TruncateRequirementsAwaitForUpdate(carupdateid int64) (orm.Result, error)
Insert(update *common.CarUpdate) (orm.Result, error)
InsertAndCreateStatus(update *common.CarUpdate) (orm.Result, error) // Insert a car update and create the appropriate update_status entry
Load(update *common.CarUpdate) error
LogStatus(update *common.CarUpdate) (orm.Result, error)
SelectByID(id int64) (*common.CarUpdate, error)
SelectByManifestID(int64) ([]common.CarUpdate, error)
SelectByVIN(vin string) ([]common.CarUpdate, error)
SelectMostRecentByVINs(vins []string) ([]common.CarUpdate, error)
SelectOrInsert(update *common.CarUpdate) (bool, error)
Select(filter *common.CarUpdate, paging *PageQueryOptions) ([]common.CarUpdate, error)
UpdateStatus(update *common.CarUpdate) (orm.Result, error)
UpdateStatusIfNotRepeat(update *common.CarUpdate) (orm.Result, error)
GetManifest(carupdateid int64) (*common.UpdateManifest, error)
HasPendingUpdates(manifestID int64, vin string) (bool, error)
HasPendingUpdatesFromAftersalesUser(manifestID int64, vin string) (updateID int64, pendingUpdateAftersales bool, err error) // Cancel any pending aftersales update
InsertMissingFlashpack(vin string, flashpackVersion string) error // Flashpack Version = OS Version Number
}
// CarUpdate query methods
type CarUpdates struct {
QueryBase
}
func (c *CarUpdates) selectFilter(query *orm.Query, filter *common.CarUpdate) {
if filter.ID > 0 {
query.Where("car_update.id = ?", filter.ID)
}
if filter.VIN != "" {
query.Where("vin = ?", filter.VIN)
}
if filter.UpdateManifestID > 0 {
query.Where("update_manifest_id = ?", filter.UpdateManifestID)
}
if filter.Status != "" {
query.Where("status = ?", filter.Status)
}
if filter.UpdateSource != "" {
query.Where("update_source = ?", filter.UpdateSource)
}
}
// StatusHistoryFilter get CarUpdates with certain status' filtered out.
// When a status is filtered out, CarUpdates with that status will still
// be returned, but will contain their last status not ignored.
func StatusHistoryFilter(query *orm.Query, ignoreStatuses []string) {
query.ColumnExpr("car_update.id")
query.ColumnExpr("car_update.vin")
query.ColumnExpr("car_update.created_at")
query.ColumnExpr("car_update.updated_at")
query.ColumnExpr("car_update.update_manifest_id")
query.ColumnExpr("car_update.error_code")
query.ColumnExpr("car_update.info")
query.ColumnExpr("car_update.username")
query.ColumnExpr("car_update_display_status.display_status AS status")
query.Join(`LEFT JOIN car_update_display_status ON car_update.id = car_update_display_status.car_update_id`)
}
// CarUpdate select by car update id
func (c *CarUpdates) SelectByID(id int64) (*common.CarUpdate, error) {
update := common.CarUpdate{ID: id}
err := c.GetDBConn().Model(&update).WherePK().
Relation("UpdateManifest").
Select()
return &update, errors.WithStack(err)
}
// SelectByVIN returns list of car updates by VIN
func (c *CarUpdates) SelectByVIN(vin string) ([]common.CarUpdate, error) {
updates := []common.CarUpdate{}
err := c.GetDBConn().Model(&updates).Where("vin = ?", vin).
Relation("UpdateManifest").
Select()
return updates, errors.WithStack(err)
}
// SelectMostRecentByVINs returns a list of most recent car update for VINs
func (c *CarUpdates) SelectMostRecentByVINs(vins []string) ([]common.CarUpdate, error) {
updates := []common.CarUpdate{}
err := c.GetDBConn().Model(&updates).
Where("vin IN (?)", pg.In(vins)).
WhereGroup(func(q *pg.Query) (*pg.Query, error) {
return q.Where("(car_update.created_at = (SELECT MAX(t2.created_at) FROM public.car_updates t2 WHERE t2.vin = car_update.vin))"), nil
}).
Relation("UpdateManifest").
Select()
return updates, errors.WithStack(err)
}
// SelectByManifestID returns list of car updates by package update id
func (c *CarUpdates) SelectByManifestID(manifest_id int64) ([]common.CarUpdate, error) {
updates := []common.CarUpdate{}
err := c.GetDBConn().Model(&updates).Where("update_manifest.id = ?", manifest_id).
Relation("UpdateManifest").
Select()
return updates, errors.WithStack(err)
}
func (c *CarUpdates) SelectOrInsert(update *common.CarUpdate) (bool, error) {
return c.insertSelectWithStack(c.GetDBConn().Model(update).Where("vin = ?vin AND update_manifest_id = ?update_manifest_id").SelectOrInsert())
}
// Select returns list of cars
func (c *CarUpdates) Select(filter *common.CarUpdate, paging *PageQueryOptions) ([]common.CarUpdate, error) {
ups := []common.CarUpdate{}
query := c.GetDBConn().Model(&ups)
c.selectFilter(query, filter)
StatusHistoryFilter(query, []string{"cleanup_succeeded"})
c.pageQuery(query, paging)
if filter.UpdateManifest != nil && filter.UpdateManifest.ManifestType > 0 {
query.
Relation("UpdateManifest", func(q *orm.Query) (*orm.Query, error) {
return q.Where("manifest_type = ?", filter.UpdateManifest.ManifestType), nil
})
} else {
query.Relation("UpdateManifest")
}
err := query.Select()
return ups, errors.WithStack(err)
}
func (c *CarUpdates) Delete(update *common.CarUpdate) (orm.Result, error) {
err := validator.ValidateIDField(update.ID)
if err != nil {
return nil, errors.WithStack(err)
}
conn := c.GetDBConn()
tx, err := conn.Begin()
if err != nil {
return nil, errors.WithStack(err)
}
defer func() {
if err != nil {
tx.Rollback()
}
tx.Close()
}()
_, err = tx.Model(&common.CarUpdateStatus{CarUpdateID: update.ID}).Where("car_update_id = ?car_update_id").Delete()
if err != nil {
return nil, errors.WithStack(err)
}
result, err := tx.Model(update).WherePK().Delete()
if err != nil {
return nil, errors.WithStack(err)
}
err = tx.Commit()
return result, errors.WithStack(err)
}
func (c *CarUpdates) UpdateStatus(update *common.CarUpdate) (orm.Result, error) {
result, err := c.UpdateCarUpdate(update)
if err != nil {
return result, err
}
_, err = c.LogStatus(update)
return result, err
}
func (c *CarUpdates) UpdateStatusIfNotRepeat(update *common.CarUpdate) (orm.Result, error) {
orm, err := c.LogStatusIfNotARepeat(update)
if err != nil {
return orm, err
}
result, err := c.UpdateCarUpdate(update)
if err != nil {
err = errors.WithStack(err)
}
return result, err
}
func (c *CarUpdates) UpdateCarUpdate(update *common.CarUpdate) (orm.Result, error) {
err := validator.ValidateIDField(update.ID)
if err != nil {
return nil, errors.WithStack(err)
}
return c.resultWithStack(c.GetDBConn().Model(update).Column("status", "error_code").WherePK().Update())
}
func (c *CarUpdates) LogStatus(update *common.CarUpdate) (orm.Result, error) {
// Should this also be updated to have the catch of double statuses
status := common.CarUpdateStatus{
CarUpdateID: update.ID,
Status: update.Status,
ErrorCode: update.ErrorCode,
Info: update.Info,
}
result, err := c.resultWithStack(c.GetDBConn().Model(&status).Insert())
if err != nil {
err = errors.WithStack(fmt.Errorf("LogStatus::%s. with status %d %s %d %s", err.Error(), status.CarUpdateID, status.Status, status.ErrorCode, status.Info))
logger.Err(err)
}
return result, err
}
// If the last status that was inserted is the same as the one we are trying to insert, we do not do it
var RepeatedStatus = errors.New("RepeatedStatus")
func (c *CarUpdates) LogStatusIfNotARepeat(update *common.CarUpdate) (orm orm.Result, err error) {
logger.Debug().Msgf("attempt to add new status %s for update %d", update.Status, update.ID) // CEC-5650 debugging
status := common.CarUpdateStatus{
CarUpdateID: update.ID,
Status: update.Status,
ErrorCode: update.ErrorCode,
Info: update.Info,
}
// This query insert's our status if the most recent status on the car_update_id does not match the new status
// If it does match the new status, then we do not insert
// 1: Update ID, 2: Status, 3: error_code, 4: The info, 5: UpdateID, 6: The status
query := `INSERT INTO public.car_update_statuses(
car_update_id, status, error_code, info)
SELECT ?, ?, ?, ? WHERE NOT EXISTS(
SELECT 1 FROM (SELECT id, car_update_id, status, error_code, created_at, updated_at, info
FROM public.car_update_statuses WHERE car_update_id = ? ORDER BY created_at DESC LIMIT 1) AS temp WHERE status = ?)`
orm, err = c.GetDBConn().Exec(query, status.CarUpdateID, status.Status, status.ErrorCode, status.Info, status.CarUpdateID, status.Status)
if err != nil {
err = errors.WithStack(fmt.Errorf("%s. with status %d %s %d %s", err.Error(), status.CarUpdateID, status.Status, status.ErrorCode, status.Info))
logger.Err(err)
return orm, err
}
if orm.RowsAffected() == 0 {
return orm, RepeatedStatus
}
return
}
func (c *CarUpdates) Insert(update *common.CarUpdate) (orm.Result, error) {
return c.insert(update)
}
func (c *CarUpdates) InsertAndCreateStatus(update *common.CarUpdate) (res orm.Result, err error) {
res, err = c.insert(update)
if err != nil {
return
}
return c.LogStatus(update)
}
func (c *CarUpdates) Load(update *common.CarUpdate) error {
query := c.GetDBConn().Model(update)
if update.ID > 0 {
query.WherePK()
} else if update.VIN == "" && update.UpdateManifestID > 0 {
query.Where("vin = ?vin AND update_manifest_id = ?update_manifest_id")
} else {
return errors.New("no id, vin, update_manifest_id")
}
if update.UpdateManifest != nil && update.UpdateManifest.ManifestType > 0 {
query.
Relation("UpdateManifest", func(q *orm.Query) (*orm.Query, error) {
return q.Where("manifest_type = ?", update.UpdateManifest.ManifestType), nil
})
} else {
query.Relation("UpdateManifest")
}
return errors.WithStack(query.
Relation("UpdateManifest.ECUs").
Relation("UpdateManifest.ECUs.Files").
Relation("UpdateManifest.ECUs.Files.WriteRegion").
Relation("UpdateManifest.ECUs.Files.EraseRegion").
Select())
}
func (c *CarUpdates) Count(filter *common.CarUpdate) (int, error) {
query := c.GetDBConn().Model((*common.CarUpdate)(nil))
c.selectFilter(query, filter)
return c.countWithStack(query.Count())
}
func (c *CarUpdates) CountUpdateStatuses(carupdateid int64) (int, error) {
query := c.GetDBConn().Model((*common.CarUpdateStatus)(nil))
return c.countWithStack(query.Where("car_update_id = ?", carupdateid).Count())
}
func (c *CarUpdates) GetUpdateStatuses(carupdateid int64, paging *PageQueryOptions) ([]common.CarUpdateStatus, error) {
result := []common.CarUpdateStatus{}
query := c.GetDBConn().Model(&result)
c.pageQuery(query, paging)
err := query.Where("car_update_id = ?", carupdateid).Select()
if err == nil && len(result) == 0 {
return result, errors.WithStack(pg.ErrNoRows)
}
return result, errors.WithStack(err)
}
func (c *CarUpdates) TruncateRequirementsAwaitForUpdate(carupdateid int64) (orm.Result, error) {
queryString := `with row_nums as (select *, row_number() over (partition by car_update_id order by updated_at) as row
from car_update_statuses where status = 'requirements_await'
and car_update_id = ?)
delete from car_update_statuses where id in (
select id from row_nums where (row != (select max(row) from row_nums)) and (row != 1)
)`
err := validator.ValidateIDField(carupdateid)
if err != nil {
return nil, errors.WithStack(err)
}
conn := c.GetDBConn()
tx, err := conn.Begin()
if err != nil {
return nil, errors.WithStack(err)
}
defer func() {
if err != nil {
tx.Rollback()
}
tx.Close()
}()
result, err := conn.Query(&common.CarUpdateStatus{}, queryString, carupdateid)
if err != nil {
return nil, errors.WithStack(err)
}
err = tx.Commit()
return result, errors.WithStack(err)
}
func (c *CarUpdates) GetManifest(carupdateid int64) (*common.UpdateManifest, error) {
manifest := common.UpdateManifest{}
err := c.GetDBConn().Model(&manifest).Join("JOIN car_updates").JoinOn("car_updates.update_manifest_id = update_manifest.id").JoinOn("car_updates.id = ?", carupdateid).Relation("ECUs").Relation("ECUs.Files").Select()
return &manifest, errors.WithStack(err)
}
func (c *CarUpdates) HasPendingUpdates(manifestID int64, vin string) (bool, error) {
count, err := c.GetDBConn().Model(&common.CarUpdate{}).Where("update_manifest_id = ? AND vin = ? AND status NOT IN (?)", manifestID, vin, pg.In(FINAL_UPDATE_STATUS)).Count()
if err != nil {
return false, err
}
return count > 0, nil
}
// An updateID of 0 means no pending update
func (c *CarUpdates) HasPendingUpdatesFromAftersalesUser(manifestID int64, vin string) (updateID int64, pendingUpdateAftersales bool, err error) {
var carUpdates []common.CarUpdate
err = c.GetDBConn().Model(&carUpdates).Where("update_manifest_id = ? AND vin = ? AND status NOT IN (?)", manifestID, vin, pg.In(FINAL_UPDATE_STATUS)).Select()
if err != nil {
return
}
if len(carUpdates) == 0 {
return
}
if len(carUpdates) > 1 {
logger.Error().Msgf("Have more than one pending update for manifestID %d on vin %s. This should not happen and our logic needs to change", manifestID, vin)
}
// This should only ever be one
for _, u := range carUpdates {
updateID = u.ID
if u.UpdateSource == common.UPDATE_SOURCE_AFTERSALES {
pendingUpdateAftersales = true
break
}
}
return
}
func (c *CarUpdates) InsertMissingFlashpack(vin string, flashpackVersion string) (err error) {
mf := common.MissingFlashpack{
VIN: vin,
FlashPackVersion: flashpackVersion,
}
_, err = c.insert(&mf)
return
}