github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/automation/run/filestore.go (about)

     1  package run
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/qri-io/qri/automation/workflow"
    11  	"github.com/qri-io/qri/base/params"
    12  	"github.com/qri-io/qri/event"
    13  	"github.com/qri-io/qri/profile"
    14  )
    15  
    16  type fileStore struct {
    17  	path  string
    18  	store *MemStore
    19  }
    20  
    21  // compile-time assertion that fileStore is a Store
    22  var _ Store = (*fileStore)(nil)
    23  
    24  // NewFileStore creates a workflow store that persists to a file
    25  func NewFileStore(repoPath string) (Store, error) {
    26  	s := &fileStore{
    27  		path:  filepath.Join(repoPath, "runs.json"),
    28  		store: NewMemStore(),
    29  	}
    30  
    31  	return s, s.loadFromFile()
    32  }
    33  
    34  func (s *fileStore) loadFromFile() error {
    35  	s.store.mu.Lock()
    36  	defer s.store.mu.Unlock()
    37  
    38  	data, err := ioutil.ReadFile(s.path)
    39  	if err != nil {
    40  		if os.IsNotExist(err) {
    41  			return nil
    42  		}
    43  		log.Debugw("fileStore loading store from file", "error", err)
    44  		return err
    45  	}
    46  	if err := json.Unmarshal(data, s.store); err != nil {
    47  		log.Debugw("fileStore deserializing from JSON", "error", err)
    48  		return err
    49  	}
    50  
    51  	return nil
    52  }
    53  
    54  func (s *fileStore) writeToFile() error {
    55  	s.store.mu.Lock()
    56  	defer s.store.mu.Unlock()
    57  	return s.writeToFileNoLock()
    58  }
    59  
    60  // Only use this when you have a surrounding lock
    61  func (s *fileStore) writeToFileNoLock() error {
    62  	data, err := json.Marshal(s.store)
    63  	if err != nil {
    64  		return err
    65  	}
    66  	return ioutil.WriteFile(s.path, data, 0644)
    67  }
    68  
    69  // Create adds a new run State to the Store
    70  func (s *fileStore) Create(ctx context.Context, r *State) (*State, error) {
    71  	return s.store.Create(ctx, r)
    72  }
    73  
    74  // Put puts a run State with an existing run ID into the Store
    75  func (s *fileStore) Put(ctx context.Context, r *State) (*State, error) { return s.store.Put(ctx, r) }
    76  
    77  // Get gets the associated run.State
    78  func (s *fileStore) Get(ctx context.Context, id string) (*State, error) { return s.store.Get(ctx, id) }
    79  
    80  // Count returns the number of runs for a given workflow.ID
    81  func (s *fileStore) Count(ctx context.Context, wid workflow.ID) (int, error) {
    82  	return s.store.Count(ctx, wid)
    83  }
    84  
    85  // List lists all the runs associated with the workflow.ID in reverse
    86  // chronological order
    87  func (s *fileStore) List(ctx context.Context, wid workflow.ID, lp params.List) ([]*State, error) {
    88  	return s.store.List(ctx, wid, lp)
    89  }
    90  
    91  // GetLatest returns the most recent run associated with the workflow id
    92  func (s *fileStore) GetLatest(ctx context.Context, wid workflow.ID) (*State, error) {
    93  	return s.store.GetLatest(ctx, wid)
    94  }
    95  
    96  // GetStatus returns the status of the latest run based on the
    97  // workflow.ID
    98  func (s *fileStore) GetStatus(ctx context.Context, wid workflow.ID) (Status, error) {
    99  	return s.store.GetStatus(ctx, wid)
   100  }
   101  
   102  // ListByStatus returns a list of run.State entries with a given status
   103  // looking only at the most recent run of each Workflow
   104  func (s *fileStore) ListByStatus(ctx context.Context, owner profile.ID, status Status, lp params.List) ([]*State, error) {
   105  	return s.store.ListByStatus(ctx, owner, status, lp)
   106  }
   107  
   108  // Shutdown writes the run events to the filestore
   109  func (s *fileStore) Shutdown() error {
   110  	if err := s.writeToFile(); err != nil {
   111  		return err
   112  	}
   113  	return s.store.Shutdown()
   114  }
   115  
   116  // AddEvent writes an event to the store, attaching it to an existing stored
   117  // run state
   118  func (s *fileStore) AddEvent(id string, e event.Event) error {
   119  	return s.store.AddEvent(id, e)
   120  }