Skip to content

Commit

Permalink
feat(fsi): read fsi-linked dataset
Browse files Browse the repository at this point in the history
  • Loading branch information
b5 committed Jul 26, 2019
1 parent d352e44 commit 74705ac
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 29 deletions.
1 change: 1 addition & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ func NewServerRoutes(s Server) *http.ServeMux {
fsih := NewFSIHandlers(s.Instance, cfg.API.ReadOnly)
m.Handle("/dsstatus/", s.middleware(fsih.StatusHandler))
m.Handle("/fsilinks/", s.middleware(fsih.LinksHandler))
m.Handle("/fsi/", s.middleware(fsih.DatasetHandler))

renderh := NewRenderHandlers(node.Repo)
m.Handle("/render/", s.middleware(renderh.RenderHandler))
Expand Down
58 changes: 45 additions & 13 deletions api/fsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/http"

util "github.com/qri-io/apiutil"
"github.com/qri-io/dataset"
"github.com/qri-io/qri/lib"
)

Expand All @@ -22,19 +23,43 @@ func NewFSIHandlers(inst *lib.Instance, readOnly bool) FSIHandlers {
}
}

// LinksHandler is the endpoint for getting the list of fsi-linked datasets
func (h *FSIHandlers) LinksHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "OPTIONS":
util.EmptyOkHandler(w, r)
case "GET":
if h.ReadOnly {
readOnlyResponse(w, "/fsilinks")
return
}
h.linksHandler(w, r)
default:
util.NotFoundHandler(w, r)
}
}

func (h *FSIHandlers) linksHandler(w http.ResponseWriter, r *http.Request) {
p := false
res := []*lib.FSILink{}
if err := h.Links(&p, &res); err != nil {
util.WriteErrResponse(w, http.StatusInternalServerError, fmt.Errorf("error listing links: %s", err.Error()))
return
}
util.WriteResponse(w, res)
}

// StatusHandler is the endpoint for getting the status of a linked dataset
func (h *FSIHandlers) StatusHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "OPTIONS":
util.EmptyOkHandler(w, r)
case "GET":
if h.ReadOnly {
readOnlyResponse(w, "/status")
readOnlyResponse(w, "/dsstatus")
return
}
h.statusHandler(w, r)
case "POST":
h.statusHandler(w, r)
default:
util.NotFoundHandler(w, r)
}
Expand All @@ -56,28 +81,35 @@ func (h *FSIHandlers) statusHandler(w http.ResponseWriter, r *http.Request) {
util.WriteResponse(w, res)
}

// LinksHandler is the endpoint for getting the list of fsi-linked datasets
func (h *FSIHandlers) LinksHandler(w http.ResponseWriter, r *http.Request) {
// DatasetHandler returns an fsi-linked dataset
func (h *FSIHandlers) DatasetHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "OPTIONS":
util.EmptyOkHandler(w, r)
case "GET":
if h.ReadOnly {
readOnlyResponse(w, "/fsilinks")
readOnlyResponse(w, "/fsi")
return
}
h.linksHandler(w, r)
h.datasetHandler(w, r)
default:
util.NotFoundHandler(w, r)
}
}

func (h *FSIHandlers) linksHandler(w http.ResponseWriter, r *http.Request) {
p := false
res := []*lib.FSILink{}
if err := h.Links(&p, &res); err != nil {
util.WriteErrResponse(w, http.StatusInternalServerError, fmt.Errorf("error listing links: %s", err.Error()))
func (h *FSIHandlers) datasetHandler(w http.ResponseWriter, r *http.Request) {
ref, err := DatasetRefFromPath(r.URL.Path[len("/fsi"):])
if err != nil {
util.WriteErrResponse(w, http.StatusBadRequest, fmt.Errorf("bad reference: %s", err.Error()))
return
}
util.WriteResponse(w, res)

str := ref.String()
ds := &dataset.Dataset{}
if err := h.FSIDatasetForRef(&str, ds); err != nil {
util.WriteErrResponse(w, http.StatusInternalServerError, err)
return
}

util.WriteResponse(w, ds)
}
58 changes: 44 additions & 14 deletions fsi/fsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@ import (
"github.com/qri-io/qri/repo"
)

// QriRefFilename links the current working folder to a dataset by containing a ref to it.
// QriRefFilename is the name of the file that links a folder to a dataset.
// The file contains a dataset reference that declares the link
// ref files are the authoritative definition of weather a folder is linked
// or not
const QriRefFilename = ".qri-ref"

// GetLinkedFilesysRef returns whether the current directory is linked to a
// GetLinkedFilesysRef returns whether a directory is linked to a
// dataset in your repo, and the reference to that dataset.
func GetLinkedFilesysRef(dir string) (string, bool) {
data, err := ioutil.ReadFile(filepath.Join(dir, QriRefFilename))
Expand Down Expand Up @@ -63,6 +66,33 @@ func (fsi *FSI) Links() ([]*Link, error) {
return fsi.load()
}

// RefLink returns a link for a given dataset reference
func (fsi *FSI) RefLink(refStr string) (*Link, error) {
links, err := fsi.load()
if err != nil {
return nil, err
}

ref, err := repo.ParseDatasetRef(refStr)
if err != nil {
return nil, err
}

if err = repo.CanonicalizeDatasetRef(fsi.repo, &ref); err != nil && err != repo.ErrNotFound {
return nil, err
}

alias := ref.AliasString()

for _, l := range links {
if l.Alias == alias {
return l, nil
}
}

return nil, repo.ErrNotFound
}

// CreateLink connects a directory
func (fsi *FSI) CreateLink(dirPath, refStr string) (string, error) {
links, err := fsi.load()
Expand Down Expand Up @@ -141,37 +171,37 @@ func (fsi *FSI) UpdateLink(dirPath, refStr string) (string, error) {
return ref.String(), err
}

// Unlink breaks the connection between a directory and a
func (fsi *FSI) Unlink(dirPath, refStr string) (string, error) {
links, err := fsi.load()
if err != nil {
return "", err
}

// Unlink breaks the connection between a directory and a dataset
func (fsi *FSI) Unlink(dirPath, refStr string) error {
ref, err := repo.ParseDatasetRef(refStr)
if err != nil {
return "", err
return err
}

if err = repo.CanonicalizeDatasetRef(fsi.repo, &ref); err != nil {
return ref.String(), err
return err
}

alias := ref.AliasString()

links, err := fsi.load()
if err != nil {
return err
}

for i, l := range links {
if l.Alias == alias {
links = links.Remove(i)

if err = removeLinkFile(dirPath); err != nil {
return "", err
return err
}

return "", fsi.save(links)
return fsi.save(links)
}
}

return "", fmt.Errorf("%s is not linked", ref)
return fmt.Errorf("%s is not linked", ref)
}

// WriteComponents writes components of the dataset to the given path, as individual files.
Expand Down
38 changes: 38 additions & 0 deletions fsi/fsi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,41 @@ func TestUpdateLink(t *testing.T) {
t.Errorf("error: link did not match, actual: %s, expect: %s", link, expect)
}
}

func TestRefLink(t *testing.T) {
paths := NewTmpPaths()
defer paths.Close()

fsi := NewFSI(paths.testRepo, paths.fsiLinkFile)

// TODO (b5) - tying canonicalization to FSI is making us do phony stuff like
// this to trick name resolution, should we just make fsi as a subsystem
// require resolved references?
refStr := "me/test_ds@/ipfs/QmExample"

_, err := fsi.RefLink(refStr)
if err != repo.ErrNotFound {
t.Errorf("expected link to non-existen ref to return not found. got: %s", err)
}

if _, err = fsi.CreateLink(paths.firstDir, "me/test_ds"); err != nil {
t.Fatalf("error creating link: %s", err.Error())
}

link, err := fsi.RefLink(refStr)
if err != nil {
t.Errorf("unexpected error fetching linked ref: %s", err)
}

if paths.firstDir != link.Path {
t.Errorf("link path mismatch. expected: %s got: %s", paths.firstDir, link.Path)
}

if err = fsi.Unlink(link.Path, refStr); err != nil {
t.Errorf("error dropping link: %s", err)
}

if _, err := fsi.RefLink(refStr); err != repo.ErrNotFound {
t.Errorf("expected unknown ref to return errNotFound. got: %s", err)
}
}
1 change: 0 additions & 1 deletion fsi/mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ func ReadDir(dir string) (ds *dataset.Dataset, fileMap map[string]string, proble
componentNameTransform: &dataset.Transform{},
componentNameViz: &dataset.Viz{},

// TODO (b5) - deal with dataset bodies
componentNameBody: nil,
}

Expand Down
27 changes: 26 additions & 1 deletion lib/fsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"path/filepath"

"github.com/qri-io/dataset"
"github.com/qri-io/dataset/dsfs"
"github.com/qri-io/qri/base"
"github.com/qri-io/qri/fsi"
Expand Down Expand Up @@ -83,7 +84,8 @@ func (m *FSIMethods) Unlink(p *LinkParams, res *string) (err error) {

// TODO (b5) - inst should have an fsi instance
fsint := fsi.NewFSI(m.inst.repo, fsi.RepoPath(m.inst.repoPath))
*res, err = fsint.Unlink(p.Dir, p.Ref)
err = fsint.Unlink(p.Dir, p.Ref)

return err
}

Expand Down Expand Up @@ -183,3 +185,26 @@ func (m *FSIMethods) Checkout(p *CheckoutParams, out *string) (err error) {
err = fsint.WriteComponents(ds, p.Dir)
return err
}

// FSIDatasetForRef reads an fsi-linked dataset for
func (m *FSIMethods) FSIDatasetForRef(refStr *string, res *dataset.Dataset) error {
if m.inst.rpc != nil {
return m.inst.rpc.Call("FSIMethods.FSIDatasetForRef", refStr, res)
}

// TODO (b5) - inst should have an fsi instance
fsint := fsi.NewFSI(m.inst.repo, fsi.RepoPath(m.inst.repoPath))

link, err := fsint.RefLink(*refStr)
if err != nil {
return err
}

ds, _, _, err := fsi.ReadDir(link.Path)
if err != nil {
return err
}

*res = *ds
return nil
}

0 comments on commit 74705ac

Please sign in to comment.