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  }