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  }