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 }