github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/base/dsfs/load.go (about) 1 package dsfs 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/qri-io/dataset" 8 "github.com/qri-io/qfs" 9 ) 10 11 // LoadDataset reads a dataset from a cafs and dereferences structure, transform, and commitMsg if they exist, 12 // returning a fully-hydrated dataset 13 func LoadDataset(ctx context.Context, store qfs.Filesystem, path string) (*dataset.Dataset, error) { 14 log.Debugw("LoadDataset", "path", path) 15 if store == nil { 16 return nil, fmt.Errorf("loading dataset: store is nil") 17 } 18 19 // set a timeout to handle long-lived requests when connected to IPFS. 20 // if we don't have the dataset locally, IPFS will reach out onto the d.web to 21 // attempt to resolve previous hashes. capping the duration yeilds quicker results. 22 // TODO (b5) - The proper way to solve this is to feed a local-only IPFS store 23 // to this entire function, or have a mechanism for specifying that a fetch 24 // must be local 25 ctx, cancel := context.WithTimeout(ctx, OpenFileTimeoutDuration) 26 defer cancel() 27 28 ds, err := LoadDatasetRefs(ctx, store, path) 29 if err != nil { 30 log.Debugf("loading dataset: %s", err) 31 return nil, fmt.Errorf("loading dataset: %w", err) 32 } 33 if err := DerefDataset(ctx, store, ds); err != nil { 34 log.Debug(err.Error()) 35 return nil, err 36 } 37 38 return ds, nil 39 } 40 41 // LoadDatasetRefs reads a dataset from a content addressed filesystem without 42 // dereferencing components 43 func LoadDatasetRefs(ctx context.Context, fs qfs.Filesystem, path string) (*dataset.Dataset, error) { 44 pathWithBasename := PackageFilepath(fs, path, PackageFileDataset) 45 log.Debugw("LoadDatasetPath", "packageFilepath", pathWithBasename) 46 data, err := fileBytes(fs.Get(ctx, pathWithBasename)) 47 if err != nil { 48 log.Debug(err.Error()) 49 return nil, fmt.Errorf("reading %s file: %w", PackageFileDataset.String(), err) 50 } 51 52 ds, err := dataset.UnmarshalDataset(data) 53 if err != nil { 54 log.Debug(err.Error()) 55 return nil, fmt.Errorf("unmarshaling %s file: %w", PackageFileDataset.String(), err) 56 } 57 58 // assign path to retain reference to the path this dataset was read from 59 ds.Path = path 60 61 return ds, nil 62 } 63 64 // DerefDataset attempts to fully dereference a dataset 65 func DerefDataset(ctx context.Context, store qfs.Filesystem, ds *dataset.Dataset) error { 66 if err := DerefMeta(ctx, store, ds); err != nil { 67 return err 68 } 69 if err := DerefStructure(ctx, store, ds); err != nil { 70 return err 71 } 72 if err := DerefTransform(ctx, store, ds); err != nil { 73 return err 74 } 75 if err := DerefViz(ctx, store, ds); err != nil { 76 return err 77 } 78 if err := DerefReadme(ctx, store, ds); err != nil { 79 return err 80 } 81 if err := DerefStats(ctx, store, ds); err != nil { 82 return err 83 } 84 return DerefCommit(ctx, store, ds) 85 } 86 87 // LoadBody loads the data this dataset points to from the store 88 func LoadBody(ctx context.Context, fs qfs.Filesystem, ds *dataset.Dataset) (qfs.File, error) { 89 return fs.Get(ctx, ds.BodyPath) 90 } 91 92 // DerefCommit derferences a dataset's Commit element if required should be a 93 // no-op if ds.Commit is nil or isn't a reference 94 func DerefCommit(ctx context.Context, store qfs.Filesystem, ds *dataset.Dataset) error { 95 if ds.Commit != nil && ds.Commit.IsEmpty() && ds.Commit.Path != "" { 96 cm, err := loadCommit(ctx, store, ds.Commit.Path) 97 if err != nil { 98 log.Debug(err.Error()) 99 return fmt.Errorf("loading dataset commit: %w", err) 100 } 101 cm.Path = ds.Commit.Path 102 ds.Commit = cm 103 } 104 return nil 105 } 106 107 func loadCommit(ctx context.Context, fs qfs.Filesystem, path string) (st *dataset.Commit, err error) { 108 data, err := fileBytes(fs.Get(ctx, path)) 109 if err != nil { 110 log.Debug(err.Error()) 111 return nil, fmt.Errorf("loading commit file: %s", err.Error()) 112 } 113 return dataset.UnmarshalCommit(data) 114 } 115 116 // DerefMeta derferences a dataset's transform element if required should be a 117 // no-op if ds.Meta is nil or isn't a reference 118 func DerefMeta(ctx context.Context, store qfs.Filesystem, ds *dataset.Dataset) error { 119 if ds.Meta != nil && ds.Meta.IsEmpty() && ds.Meta.Path != "" { 120 md, err := loadMeta(ctx, store, ds.Meta.Path) 121 if err != nil { 122 log.Debug(err.Error()) 123 return fmt.Errorf("loading dataset metadata: %w", err) 124 } 125 md.Path = ds.Meta.Path 126 ds.Meta = md 127 } 128 return nil 129 } 130 131 func loadMeta(ctx context.Context, fs qfs.Filesystem, path string) (md *dataset.Meta, err error) { 132 data, err := fileBytes(fs.Get(ctx, path)) 133 if err != nil { 134 log.Debug(err.Error()) 135 return nil, fmt.Errorf("loading metadata file: %w", err) 136 } 137 md = &dataset.Meta{} 138 err = md.UnmarshalJSON(data) 139 return md, err 140 } 141 142 // DerefReadme dereferences a dataset's Readme element if required no-op if 143 // ds.Readme is nil or isn't a reference 144 func DerefReadme(ctx context.Context, store qfs.Filesystem, ds *dataset.Dataset) error { 145 if ds.Readme != nil && ds.Readme.IsEmpty() && ds.Readme.Path != "" { 146 rm, err := loadReadme(ctx, store, ds.Readme.Path) 147 if err != nil { 148 log.Debug(err.Error()) 149 return fmt.Errorf("loading dataset readme: %w", err) 150 } 151 rm.Path = ds.Readme.Path 152 ds.Readme = rm 153 } 154 return nil 155 } 156 157 func loadReadme(ctx context.Context, fs qfs.Filesystem, path string) (st *dataset.Readme, err error) { 158 data, err := fileBytes(fs.Get(ctx, path)) 159 if err != nil { 160 log.Debug(err.Error()) 161 return nil, fmt.Errorf("error loading readme file: %w", err) 162 } 163 return dataset.UnmarshalReadme(data) 164 } 165 166 // LoadReadmeScript loads script data from a dataset path if the given dataset has a readme script is specified 167 // the returned qfs.File will be the value of dataset.Readme.ScriptPath 168 func LoadReadmeScript(ctx context.Context, fs qfs.Filesystem, dspath string) (qfs.File, error) { 169 ds, err := LoadDataset(ctx, fs, dspath) 170 if err != nil { 171 return nil, err 172 } 173 174 if ds.Readme == nil || ds.Readme.ScriptPath == "" { 175 return nil, ErrNoReadme 176 } 177 178 return fs.Get(ctx, ds.Readme.ScriptPath) 179 } 180 181 // DerefStats derferences a dataset's stats component if required 182 // no-op if ds.Stats is nil or isn't a reference 183 func DerefStats(ctx context.Context, store qfs.Filesystem, ds *dataset.Dataset) error { 184 if ds.Stats != nil && ds.Stats.IsEmpty() && ds.Stats.Path != "" { 185 sa, err := loadStats(ctx, store, ds.Stats.Path) 186 if err != nil { 187 log.Debug(err) 188 return fmt.Errorf("loading stats component: %w", err) 189 } 190 sa.Path = ds.Stats.Path 191 ds.Stats = sa 192 } 193 return nil 194 } 195 196 func loadStats(ctx context.Context, fs qfs.Filesystem, path string) (sa *dataset.Stats, err error) { 197 data, err := fileBytes(fs.Get(ctx, path)) 198 if err != nil { 199 log.Debug(err.Error()) 200 return nil, fmt.Errorf("loading stats file: %w", err) 201 } 202 sa = &dataset.Stats{} 203 err = sa.UnmarshalJSON(data) 204 return sa, err 205 } 206 207 // DerefTransform derferences a dataset's transform element if required 208 // should be a no-op if ds.Structure is nil or isn't a reference 209 func DerefTransform(ctx context.Context, store qfs.Filesystem, ds *dataset.Dataset) error { 210 if ds.Transform != nil && ds.Transform.IsEmpty() && ds.Transform.Path != "" { 211 t, err := loadTransform(ctx, store, ds.Transform.Path) 212 if err != nil { 213 log.Debug(err.Error()) 214 return fmt.Errorf("loading dataset transform: %w", err) 215 } 216 t.Path = ds.Transform.Path 217 ds.Transform = t 218 } 219 return nil 220 } 221 222 func loadTransform(ctx context.Context, fs qfs.Filesystem, path string) (q *dataset.Transform, err error) { 223 data, err := fileBytes(fs.Get(ctx, path)) 224 if err != nil { 225 log.Debug(err.Error()) 226 return nil, fmt.Errorf("error loading transform raw data: %s", err.Error()) 227 } 228 229 return dataset.UnmarshalTransform(data) 230 } 231 232 // DerefStructure derferences a dataset's structure element if required 233 // should be a no-op if ds.Structure is nil or isn't a reference 234 func DerefStructure(ctx context.Context, store qfs.Filesystem, ds *dataset.Dataset) error { 235 if ds.Structure != nil && ds.Structure.IsEmpty() && ds.Structure.Path != "" { 236 st, err := loadStructure(ctx, store, ds.Structure.Path) 237 if err != nil { 238 log.Debug(err.Error()) 239 return fmt.Errorf("loading dataset structure: %w", err) 240 } 241 // assign path to retain internal reference to path 242 st.Path = ds.Structure.Path 243 ds.Structure = st 244 } 245 return nil 246 } 247 248 func loadStructure(ctx context.Context, fs qfs.Filesystem, path string) (st *dataset.Structure, err error) { 249 data, err := fileBytes(fs.Get(ctx, path)) 250 if err != nil { 251 log.Debug(err.Error()) 252 return nil, fmt.Errorf("error loading structure file: %s", err.Error()) 253 } 254 return dataset.UnmarshalStructure(data) 255 } 256 257 // DerefViz dereferences a dataset's Viz element if required 258 // no-op if ds.Viz is nil or isn't a reference 259 func DerefViz(ctx context.Context, store qfs.Filesystem, ds *dataset.Dataset) error { 260 if ds.Viz != nil && ds.Viz.IsEmpty() && ds.Viz.Path != "" { 261 vz, err := loadViz(ctx, store, ds.Viz.Path) 262 if err != nil { 263 log.Debug(err.Error()) 264 return fmt.Errorf("loading dataset viz: %w", err) 265 } 266 vz.Path = ds.Viz.Path 267 ds.Viz = vz 268 } 269 return nil 270 } 271 272 func loadViz(ctx context.Context, fs qfs.Filesystem, path string) (st *dataset.Viz, err error) { 273 data, err := fileBytes(fs.Get(ctx, path)) 274 if err != nil { 275 log.Debug(err.Error()) 276 return nil, fmt.Errorf("error loading viz file: %s", err.Error()) 277 } 278 return dataset.UnmarshalViz(data) 279 }