github.com/leg100/ots@v0.0.7-0.20210919080622-034055ced4bd/sqlite/run.go (about) 1 package sqlite 2 3 import ( 4 "github.com/leg100/ots" 5 "gorm.io/gorm" 6 "gorm.io/gorm/clause" 7 ) 8 9 var _ ots.RunStore = (*RunDB)(nil) 10 11 type RunDB struct { 12 *gorm.DB 13 } 14 15 func NewRunDB(db *gorm.DB) *RunDB { 16 return &RunDB{ 17 DB: db, 18 } 19 } 20 21 // Create persists a Run to the DB. 22 func (db RunDB) Create(domain *ots.Run) (*ots.Run, error) { 23 model := NewFromDomain(domain) 24 25 if result := db.Omit("Workspace", "ConfigurationVersion").Create(model); result.Error != nil { 26 return nil, result.Error 27 } 28 29 return model.ToDomain(), nil 30 } 31 32 // Update persists an updated Run to the DB. The existing run is fetched from 33 // the DB, the supplied func is invoked on the run, and the updated run is 34 // persisted back to the DB. The returned Run includes any changes, including a 35 // new UpdatedAt value. 36 func (db RunDB) Update(id string, fn func(*ots.Run) error) (*ots.Run, error) { 37 var model *Run 38 39 err := db.Transaction(func(tx *gorm.DB) (err error) { 40 // DB -> model 41 model, err = getRun(tx, ots.RunGetOptions{ID: &id}) 42 if err != nil { 43 return err 44 } 45 46 // Update obj using client-supplied fn 47 if err := model.Update(fn); err != nil { 48 return err 49 } 50 51 // Save changes to fields of relations (plan, apply) too 52 if result := tx.Session(&gorm.Session{FullSaveAssociations: true}).Save(model); result.Error != nil { 53 return err 54 } 55 56 return nil 57 }) 58 if err != nil { 59 return nil, err 60 } 61 62 return model.ToDomain(), nil 63 } 64 65 func (db RunDB) List(opts ots.RunListOptions) (*ots.RunList, error) { 66 var models RunList 67 var count int64 68 69 err := db.Transaction(func(tx *gorm.DB) error { 70 query := tx 71 72 // Optionally filter by workspace 73 if opts.WorkspaceID != nil { 74 ws, err := getWorkspace(tx, ots.WorkspaceSpecifier{ID: opts.WorkspaceID}) 75 if err != nil { 76 return err 77 } 78 79 query = query.Where("workspace_id = ?", ws.Model.ID) 80 } 81 82 // Optionally filter by statuses 83 if len(opts.Statuses) > 0 { 84 query = query.Where("status IN ?", opts.Statuses) 85 } 86 87 if result := query.Model(&models).Count(&count); result.Error != nil { 88 return result.Error 89 } 90 91 if result := query.Preload(clause.Associations).Scopes(paginate(opts.ListOptions)).Find(&models); result.Error != nil { 92 return result.Error 93 } 94 95 return nil 96 }) 97 if err != nil { 98 return nil, err 99 } 100 101 return &ots.RunList{ 102 Items: models.ToDomain(), 103 Pagination: ots.NewPagination(opts.ListOptions, int(count)), 104 }, nil 105 } 106 107 // Get retrieves a Run domain obj 108 func (db RunDB) Get(opts ots.RunGetOptions) (*ots.Run, error) { 109 run, err := getRun(db.DB, opts) 110 if err != nil { 111 return nil, err 112 } 113 return run.ToDomain(), nil 114 } 115 116 func getRun(db *gorm.DB, opts ots.RunGetOptions) (*Run, error) { 117 var model Run 118 119 query := db.Preload(clause.Associations) 120 121 switch { 122 case opts.ID != nil: 123 query = query.Where("external_id = ?", opts.ID) 124 case opts.PlanID != nil: 125 query = query.Joins("JOIN plans ON plans.run_id = runs.id").Where("plans.external_id = ?", opts.PlanID) 126 case opts.ApplyID != nil: 127 query = query.Joins("JOIN applies ON applies.run_id = runs.id").Where("applies.external_id = ?", opts.ApplyID) 128 default: 129 return nil, ots.ErrInvalidRunGetOptions 130 } 131 132 if result := query.First(&model); result.Error != nil { 133 return nil, result.Error 134 } 135 136 return &model, nil 137 }