diff --git a/api/types.go b/api/types.go index acb18f7ac40..b86501372b5 100644 --- a/api/types.go +++ b/api/types.go @@ -220,8 +220,10 @@ type ExportRef struct { Root cid.Cid // DAGs array specifies a list of DAGs to export - // - If exporting into a car file, defines car roots - // - If exporting into unixfs files, only one DAG is supported, DataSelector is only used to find the root node + // - If exporting into unixfs files, only one DAG is supported, DataSelector is only used to find the targeted root node + // - If exporting into a car file + // - When exactly one text-path DataSelector is specified exports the subgraph and its full merkle-path from the original root + // - Otherwise ( multiple paths and/or JSON selector specs) determines each individual subroot and exports the subtrees as a multi-root car // - When not specified defaults to a single DAG: // - Data - the entire DAG: `{"R":{"l":{"none":{}},":>":{"a":{">":{"@":{}}}}}}` DAGs []DagSpec diff --git a/node/impl/client/client.go b/node/impl/client/client.go index 3bbdd3864e6..d3535e5ddb7 100644 --- a/node/impl/client/client.go +++ b/node/impl/client/client.go @@ -1038,71 +1038,70 @@ func parseDagSpec(ctx context.Context, root cid.Cid, dsp []api.DagSpec, ds forma out := make([]dagSpec, len(dsp)) for i, spec := range dsp { - // if a selector is specified, find it's root node - if spec.DataSelector != nil { - var rsn ipld.Node - - if strings.HasPrefix(string(*spec.DataSelector), "{") { - var err error - rsn, err = selectorparse.ParseJSONSelector(string(*spec.DataSelector)) - if err != nil { - return nil, xerrors.Errorf("failed to parse json-selector '%s': %w", *spec.DataSelector, err) - } - } else { - selspec, _ := textselector.SelectorSpecFromPath(textselector.Expression(*spec.DataSelector), nil) //nolint:errcheck - rsn = selspec.Node() - } - - var newRoot cid.Cid - - var errHalt = errors.New("halt walk") + if spec.DataSelector == nil { + return nil, xerrors.Errorf("invalid DagSpec at position %d: `DataSelector` can not be nil", i) + } - if err := utils.TraverseDag( - ctx, - ds, - root, - rsn, - func(p traversal.Progress, n ipld.Node, r traversal.VisitReason) error { - if r == traversal.VisitReason_SelectionMatch { - if rootOnNodeBoundary && p.LastBlock.Path.String() != p.Path.String() { - return xerrors.Errorf("unsupported selection path '%s' does not correspond to a block boundary (a.k.a. CID link)", p.Path.String()) - } + // reify selector + var err error + out[i].selector, err = getDataSelector(spec.DataSelector) + if err != nil { + return nil, err + } - if p.LastBlock.Link == nil { - // this is likely the root node that we've matched here - newRoot = root - return errHalt - } + // find the pointed-at root node within the containing ds + var rsn ipld.Node - cidLnk, castOK := p.LastBlock.Link.(cidlink.Link) - if !castOK { - return xerrors.Errorf("cidlink cast unexpectedly failed on '%s'", p.LastBlock.Link) - } + if strings.HasPrefix(string(*spec.DataSelector), "{") { + var err error + rsn, err = selectorparse.ParseJSONSelector(string(*spec.DataSelector)) + if err != nil { + return nil, xerrors.Errorf("failed to parse json-selector '%s': %w", *spec.DataSelector, err) + } + } else { + selspec, _ := textselector.SelectorSpecFromPath(textselector.Expression(*spec.DataSelector), nil) //nolint:errcheck + rsn = selspec.Node() + } - newRoot = cidLnk.Cid + var newRoot cid.Cid + var errHalt = errors.New("halt walk") + if err := utils.TraverseDag( + ctx, + ds, + root, + rsn, + func(p traversal.Progress, n ipld.Node, r traversal.VisitReason) error { + if r == traversal.VisitReason_SelectionMatch { + if rootOnNodeBoundary && p.LastBlock.Path.String() != p.Path.String() { + return xerrors.Errorf("unsupported selection path '%s' does not correspond to a block boundary (a.k.a. CID link)", p.Path.String()) + } + if p.LastBlock.Link == nil { + // this is likely the root node that we've matched here + newRoot = root return errHalt } - return nil - }, - ); err != nil && err != errHalt { - return nil, xerrors.Errorf("error while locating partial retrieval sub-root: %w", err) - } - if newRoot == cid.Undef { - return nil, xerrors.Errorf("path selection does not match a node within %s", root) - } + cidLnk, castOK := p.LastBlock.Link.(cidlink.Link) + if !castOK { + return xerrors.Errorf("cidlink cast unexpectedly failed on '%s'", p.LastBlock.Link) + } + + newRoot = cidLnk.Cid - out[i].root = newRoot + return errHalt + } + return nil + }, + ); err != nil && err != errHalt { + return nil, xerrors.Errorf("error while locating partial retrieval sub-root: %w", err) } - if spec.DataSelector != nil { - var err error - out[i].selector, err = getDataSelector(spec.DataSelector) - if err != nil { - return nil, err - } + if newRoot == cid.Undef { + return nil, xerrors.Errorf("path selection does not match a node within %s", root) } + + out[i].root = newRoot } return out, nil