diff --git a/api/handlers/history.go b/api/handlers/history.go index c34e5d502..e8891b0a2 100644 --- a/api/handlers/history.go +++ b/api/handlers/history.go @@ -17,7 +17,7 @@ type HistoryHandlers struct { } func NewHistoryHandlers(log logging.Logger, r repo.Repo) *HistoryHandlers { - req := core.NewHistoryRequests(r) + req := core.NewHistoryRequests(r, nil) h := HistoryHandlers{*req, log} return &h } diff --git a/api/handlers/peers.go b/api/handlers/peers.go index 0bb353872..2c60cc89f 100644 --- a/api/handlers/peers.go +++ b/api/handlers/peers.go @@ -14,7 +14,7 @@ import ( ) func NewPeerHandlers(log logging.Logger, r repo.Repo, node *p2p.QriNode) *PeerHandlers { - req := core.NewPeerRequests(r, node) + req := core.NewPeerRequests(node, nil) h := PeerHandlers{*req, log} return &h } diff --git a/api/handlers/profile.go b/api/handlers/profile.go index 5ebe38f84..a4b31a3f4 100644 --- a/api/handlers/profile.go +++ b/api/handlers/profile.go @@ -18,7 +18,7 @@ type ProfileHandlers struct { } func NewProfileHandlers(log logging.Logger, r repo.Repo) *ProfileHandlers { - req := core.NewProfileRequests(r) + req := core.NewProfileRequests(r, nil) h := ProfileHandlers{*req, log} return &h } @@ -38,7 +38,7 @@ func (h *ProfileHandlers) ProfileHandler(w http.ResponseWriter, r *http.Request) func (h *ProfileHandlers) getProfileHandler(w http.ResponseWriter, r *http.Request) { args := true - res := &profile.Profile{} + res := &core.Profile{} if err := h.GetProfile(&args, res); err != nil { h.log.Infof("error getting profile: %s", err.Error()) util.WriteErrResponse(w, http.StatusInternalServerError, err) @@ -49,12 +49,12 @@ func (h *ProfileHandlers) getProfileHandler(w http.ResponseWriter, r *http.Reque } func (h *ProfileHandlers) saveProfileHandler(w http.ResponseWriter, r *http.Request) { - p := &profile.Profile{} + p := &core.Profile{} if err := json.NewDecoder(r.Body).Decode(p); err != nil { util.WriteErrResponse(w, http.StatusBadRequest, err) return } - res := &profile.Profile{} + res := &core.Profile{} if err := h.SaveProfile(p, res); err != nil { util.WriteErrResponse(w, http.StatusInternalServerError, err) return diff --git a/api/handlers/queries.go b/api/handlers/queries.go index 9d346d05b..29a13cac8 100644 --- a/api/handlers/queries.go +++ b/api/handlers/queries.go @@ -12,7 +12,7 @@ import ( ) func NewQueryHandlers(log logging.Logger, r repo.Repo) *QueryHandlers { - req := core.NewQueryRequests(r) + req := core.NewQueryRequests(r, nil) return &QueryHandlers{*req, log} } diff --git a/api/handlers/search.go b/api/handlers/search.go index c0b893f92..52bb48677 100644 --- a/api/handlers/search.go +++ b/api/handlers/search.go @@ -17,7 +17,7 @@ type SearchHandlers struct { } func NewSearchHandlers(log logging.Logger, r repo.Repo) *SearchHandlers { - req := core.NewSearchRequests(r) + req := core.NewSearchRequests(r, nil) return &SearchHandlers{*req, log} } diff --git a/cmd/profile.go b/cmd/profile.go new file mode 100644 index 000000000..8963dc1b3 --- /dev/null +++ b/cmd/profile.go @@ -0,0 +1,74 @@ +package cmd + +import ( + "encoding/json" + "os" + + "github.com/qri-io/qri/core" + "github.com/spf13/cobra" +) + +var ( + setProfileFilepath string +) + +// profileCmd represents the profile command +var profileCmd = &cobra.Command{ + Use: "profile", + Short: "show or edit user profile information", +} + +var profileGetCmd = &cobra.Command{ + Use: "get", + Short: "get profile info", + Run: func(cmd *cobra.Command, args []string) { + r, err := ProfileRequests(false) + ExitIfErr(err) + + in := true + res := &core.Profile{} + err = r.GetProfile(&in, res) + ExitIfErr(err) + + data, err := json.MarshalIndent(res, "", " ") + ExitIfErr(err) + PrintSuccess(string(data)) + }, +} + +var profileSetCmd = &cobra.Command{ + Use: "set", + Short: "add peers to the profile list", + Run: func(cmd *cobra.Command, args []string) { + var ( + dataFile *os.File + err error + ) + + r, err := ProfileRequests(false) + ExitIfErr(err) + + dataFile, err = loadFileIfPath(setProfileFilepath) + ExitIfErr(err) + + p := &core.Profile{} + err = json.NewDecoder(dataFile).Decode(p) + ExitIfErr(err) + + res := &core.Profile{} + err = r.SaveProfile(p, res) + ExitIfErr(err) + + data, err := json.MarshalIndent(res, "", " ") + ExitIfErr(err) + PrintSuccess(string(data)) + }, +} + +func init() { + profileSetCmd.Flags().StringVarP(&setProfileFilepath, "file", "f", "", "json file to update profile info") + + profileCmd.AddCommand(profileGetCmd) + profileCmd.AddCommand(profileSetCmd) + RootCmd.AddCommand(profileCmd) +} diff --git a/cmd/queries.go b/cmd/queries.go index c3d824157..351601f07 100644 --- a/cmd/queries.go +++ b/cmd/queries.go @@ -14,11 +14,12 @@ var queriesCmd = &cobra.Command{ Long: ``, Run: func(cmd *cobra.Command, args []string) { if len(args) == 0 { - req := core.NewQueryRequests(GetRepo(false)) + req, err := QueryRequests(false) + ExitIfErr(err) p := core.NewListParams("-created", pageNum, pageSize) res := []*repo.DatasetRef{} - err := req.List(&p, &res) + err = req.List(&p, &res) ExitIfErr(err) for i, q := range res { diff --git a/cmd/repo.go b/cmd/repo.go index 513287cff..4de25c1bb 100644 --- a/cmd/repo.go +++ b/cmd/repo.go @@ -48,6 +48,30 @@ func DatasetRequests(online bool) (*core.DatasetRequests, error) { return core.NewDatasetRequests(r, cli), nil } +func QueryRequests(online bool) (*core.QueryRequests, error) { + r, cli, err := RepoOrClient(online) + if err != nil { + return nil, err + } + return core.NewQueryRequests(r, cli), nil +} + +func ProfileRequests(online bool) (*core.ProfileRequests, error) { + r, cli, err := RepoOrClient(online) + if err != nil { + return nil, err + } + return core.NewProfileRequests(r, cli), nil +} + +func SearchRequests(online bool) (*core.SearchRequests, error) { + r, cli, err := RepoOrClient(online) + if err != nil { + return nil, err + } + return core.NewSearchRequests(r, cli), nil +} + // RepoOrClient returns either a func RepoOrClient(online bool) (repo.Repo, *rpc.Client, error) { if fs, err := ipfs.NewFilestore(func(cfg *ipfs.StoreCfg) { diff --git a/cmd/run.go b/cmd/run.go index 1b14ef74f..79bc3beb8 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -27,8 +27,8 @@ var runCmd = &cobra.Command{ ErrExit(fmt.Errorf("Please provide a query string to execute")) } - r := GetRepo(false) - req := core.NewQueryRequests(r) + req, err := QueryRequests(false) + ExitIfErr(err) format, err := dataset.ParseDataFormatString(cmd.Flag("format").Value.String()) if err != nil { diff --git a/cmd/search.go b/cmd/search.go index 760a98ed0..8887b3a83 100644 --- a/cmd/search.go +++ b/cmd/search.go @@ -24,7 +24,8 @@ var searchCmd = &cobra.Command{ ErrExit(fmt.Errorf("wrong number of arguments. expected qri search [query]")) } - req := core.NewSearchRequests(GetRepo(false)) + req, err := SearchRequests(false) + ExitIfErr(err) if searchCmdReindex { PrintInfo("building index...") @@ -44,7 +45,7 @@ var searchCmd = &cobra.Command{ } res := []*repo.DatasetRef{} - err := req.Search(p, &res) + err = req.Search(p, &res) ExitIfErr(err) outformat := cmd.Flag("format").Value.String() diff --git a/core/core.go b/core/core.go index 8702feecb..17cb1f257 100644 --- a/core/core.go +++ b/core/core.go @@ -17,11 +17,11 @@ func Receivers(node *p2p.QriNode) []CoreRequests { r := node.Repo return []CoreRequests{ NewDatasetRequests(r, nil), - NewHistoryRequests(r), - NewPeerRequests(r, node), - NewProfileRequests(r), - NewQueryRequests(r), - NewSearchRequests(r), + NewHistoryRequests(r, nil), + NewPeerRequests(node, nil), + NewProfileRequests(r, nil), + NewQueryRequests(r, nil), + NewSearchRequests(r, nil), } } diff --git a/core/history.go b/core/history.go index 2d5eef335..63c08a81a 100644 --- a/core/history.go +++ b/core/history.go @@ -2,6 +2,7 @@ package core import ( "fmt" + "net/rpc" "github.com/ipfs/go-datastore" "github.com/qri-io/dataset/dsfs" @@ -10,11 +11,16 @@ import ( type HistoryRequests struct { repo repo.Repo + cli *rpc.Client } func (d HistoryRequests) CoreRequestsName() string { return "history" } -func NewHistoryRequests(r repo.Repo) *HistoryRequests { +func NewHistoryRequests(r repo.Repo, cli *rpc.Client) *HistoryRequests { + if r != nil && cli != nil { + panic(fmt.Errorf("both repo and client supplied to NewHistoryRequests")) + } + return &HistoryRequests{ repo: r, } @@ -26,6 +32,10 @@ type LogParams struct { } func (d *HistoryRequests) Log(params *LogParams, res *[]*repo.DatasetRef) (err error) { + if d.cli != nil { + return d.cli.Call("HistoryRequests.Log", params, res) + } + log := []*repo.DatasetRef{} limit := params.Limit ref := &repo.DatasetRef{Path: params.Path} diff --git a/core/peers.go b/core/peers.go index 0f8ccad62..6b565da3e 100644 --- a/core/peers.go +++ b/core/peers.go @@ -3,6 +3,7 @@ package core import ( "encoding/json" "fmt" + "net/rpc" "github.com/ipfs/go-datastore/query" "github.com/qri-io/qri/p2p" @@ -12,30 +13,39 @@ import ( peer "gx/ipfs/QmXYjuNuxVzXKJCfWasQk1RqkhVLDM9jtUKhqc2WPQmFSB/go-libp2p-peer" ) -func NewPeerRequests(r repo.Repo, node *p2p.QriNode) *PeerRequests { +type PeerRequests struct { + qriNode *p2p.QriNode + cli *rpc.Client +} + +func NewPeerRequests(node *p2p.QriNode, cli *rpc.Client) *PeerRequests { + if node != nil && cli != nil { + panic(fmt.Errorf("both node and client supplied to NewPeerRequests")) + } + return &PeerRequests{ - repo: r, qriNode: node, + cli: cli, } } -type PeerRequests struct { - repo repo.Repo - qriNode *p2p.QriNode -} - func (d PeerRequests) CoreRequestsName() string { return "peers" } func (d *PeerRequests) List(p *ListParams, res *[]*profile.Profile) error { + if d.cli != nil { + return d.cli.Call("PeerRequests.List", p, res) + } + + r := d.qriNode.Repo replies := make([]*profile.Profile, p.Limit) i := 0 - user, err := d.repo.Profile() + user, err := r.Profile() if err != nil { return err } - ps, err := repo.QueryPeers(d.repo.Peers(), query.Query{}) + ps, err := repo.QueryPeers(r.Peers(), query.Query{}) if err != nil { return fmt.Errorf("error querying peers: %s", err.Error()) } @@ -56,16 +66,24 @@ func (d *PeerRequests) List(p *ListParams, res *[]*profile.Profile) error { } func (d *PeerRequests) ConnectedPeers(limit *int, peers *[]string) error { + if d.cli != nil { + return d.cli.Call("PeerRequests.ConnectedPeers", limit, peers) + } + *peers = d.qriNode.ConnectedPeers() return nil } func (d *PeerRequests) ConnectToPeer(pid *peer.ID, res *profile.Profile) error { + if d.cli != nil { + return d.cli.Call("PeerRequests.ConnectToPeer", pid, res) + } + if err := d.qriNode.ConnectToPeer(*pid); err != nil { return fmt.Errorf("error connecting to peer: %s", err.Error()) } - profile, err := d.repo.Peers().GetPeer(*pid) + profile, err := d.qriNode.Repo.Peers().GetPeer(*pid) if err != nil { return fmt.Errorf("error getting peer profile: %s", err.Error()) } @@ -75,6 +93,10 @@ func (d *PeerRequests) ConnectToPeer(pid *peer.ID, res *profile.Profile) error { } func (d *PeerRequests) Get(p *GetParams, res *profile.Profile) error { + if d.cli != nil { + return d.cli.Call("PeerRequests.Get", p, res) + } + // TODO - restore // peers, err := d.repo.Peers() // if err != nil { @@ -104,12 +126,16 @@ type NamespaceParams struct { } func (d *PeerRequests) GetNamespace(p *NamespaceParams, res *[]*repo.DatasetRef) error { + if d.cli != nil { + return d.cli.Call("PeerRequests.GetNamespace", p, res) + } + id, err := peer.IDB58Decode(p.PeerId) if err != nil { return fmt.Errorf("error decoding peer Id: %s", err.Error()) } - profile, err := d.repo.Peers().GetPeer(id) + profile, err := d.qriNode.Repo.Peers().GetPeer(id) if err != nil || profile == nil { return err } diff --git a/core/profile.go b/core/profile.go index 92dc58aca..fb05673bc 100644 --- a/core/profile.go +++ b/core/profile.go @@ -1,10 +1,13 @@ package core import ( + "encoding/json" "fmt" "io" "io/ioutil" "net/http" + "net/rpc" + "time" "github.com/qri-io/cafs/memfs" "github.com/qri-io/qri/repo" @@ -13,31 +16,101 @@ import ( type ProfileRequests struct { repo repo.Repo + cli *rpc.Client } func (d ProfileRequests) CoreRequestsName() string { return "profile" } -func NewProfileRequests(r repo.Repo) *ProfileRequests { +func NewProfileRequests(r repo.Repo, cli *rpc.Client) *ProfileRequests { + if r != nil && cli != nil { + panic(fmt.Errorf("both repo and client supplied to NewProfileRequests")) + } + return &ProfileRequests{ repo: r, + cli: cli, } } -func (r *ProfileRequests) GetProfile(in *bool, res *profile.Profile) error { +type Profile struct { + Id string `json:"id"` + Created time.Time `json:"created,omitempty"` + Updated time.Time `json:"updated,omitempty"` + Username string `json:"username"` + Type profile.UserType `json:"type"` + Email string `json:"email"` + Name string `json:"name"` + Description string `json:"description"` + HomeUrl string `json:"homeUrl"` + Color string `json:"color"` + Thumb string `json:"thumb"` + Profile string `json:"profile"` + Poster string `json:"poster"` + Twitter string `json:"twitter"` +} + +func unmarshalProfile(p *Profile) (*profile.Profile, error) { + // silly workaround for gob encoding + data, err := json.Marshal(p) + if err != nil { + return nil, fmt.Errorf("err re-encoding json: %s", err.Error()) + } + + _p := &profile.Profile{} + if err := json.Unmarshal(data, _p); err != nil { + return nil, fmt.Errorf("error unmarshaling json: %s", err.Error()) + } + + return _p, nil +} + +func marshalProfile(p *profile.Profile) (*Profile, error) { + // silly workaround for gob encoding + data, err := json.Marshal(p) + if err != nil { + return nil, fmt.Errorf("err re-encoding json: %s", err.Error()) + } + + _p := &Profile{} + if err := json.Unmarshal(data, _p); err != nil { + return nil, fmt.Errorf("error unmarshaling json: %s", err.Error()) + } + + return _p, nil +} + +func (r *ProfileRequests) GetProfile(in *bool, res *Profile) error { + if r.cli != nil { + return r.cli.Call("ProfileRequests.GetProfile", in, res) + } + profile, err := r.repo.Profile() if err != nil { return err } - *res = *profile + + _p, err := marshalProfile(profile) + if err != nil { + return err + } + *res = *_p return nil } -func (r *ProfileRequests) SaveProfile(p *profile.Profile, res *profile.Profile) error { +func (r *ProfileRequests) SaveProfile(p *Profile, res *Profile) error { + if r.cli != nil { + return r.cli.Call("ProfileRequests.SaveProfile", p, res) + } if p == nil { return fmt.Errorf("profile required for update") } - if err := r.repo.SaveProfile(p); err != nil { + _p, err := unmarshalProfile(p) + if err != nil { + return err + } + + if err := r.repo.SaveProfile(_p); err != nil { return err } @@ -46,7 +119,12 @@ func (r *ProfileRequests) SaveProfile(p *profile.Profile, res *profile.Profile) return err } - *res = *profile + p2, err := marshalProfile(profile) + if err != nil { + return err + } + + *res = *p2 return nil } @@ -58,6 +136,10 @@ type FileParams struct { } func (r *ProfileRequests) SetProfilePhoto(p *FileParams, res *profile.Profile) error { + if r.cli != nil { + return r.cli.Call("ProfileRequests.SetProfilePhoto", p, res) + } + if p.Data == nil { return fmt.Errorf("file is required") } @@ -99,6 +181,10 @@ func (r *ProfileRequests) SetProfilePhoto(p *FileParams, res *profile.Profile) e } func (r *ProfileRequests) SetPosterPhoto(p *FileParams, res *profile.Profile) error { + if r.cli != nil { + return r.cli.Call("ProfileRequests.SetPosterPhoto", p, res) + } + if p.Data == nil { return fmt.Errorf("file is required") } diff --git a/core/queries.go b/core/queries.go index f99359413..8309694c7 100644 --- a/core/queries.go +++ b/core/queries.go @@ -2,6 +2,7 @@ package core import ( "fmt" + "net/rpc" "time" "github.com/ipfs/go-datastore" @@ -14,17 +15,27 @@ import ( type QueryRequests struct { repo repo.Repo + cli *rpc.Client } func (d QueryRequests) CoreRequestsName() string { return "queries" } -func NewQueryRequests(r repo.Repo) *QueryRequests { +func NewQueryRequests(r repo.Repo, cli *rpc.Client) *QueryRequests { + if r != nil && cli != nil { + panic(fmt.Errorf("both repo and client supplied to NewQueryRequests")) + } + return &QueryRequests{ repo: r, + cli: cli, } } func (d *QueryRequests) List(p *ListParams, res *[]*repo.DatasetRef) error { + if d.cli != nil { + return d.cli.Call("QueryRequests.List", p, res) + } + results, err := d.repo.GetQueryLogs(p.Limit, p.Offset) if err != nil { return fmt.Errorf("error getting query logs: %s", err.Error()) @@ -57,7 +68,11 @@ type GetQueryParams struct { } func (d *QueryRequests) Get(p *GetQueryParams, res *dataset.Dataset) error { - // TODO - huh? do we even need to load queries + if d.cli != nil { + return d.cli.Call("QueryRequests.Get", p, res) + } + + // TODO - huh? do we even need to load query datasets? q, err := dsfs.LoadDataset(d.repo.Store(), datastore.NewKey(p.Path)) if err != nil { return fmt.Errorf("error loading dataset: %s", err.Error()) @@ -75,6 +90,10 @@ type RunParams struct { } func (r *QueryRequests) Run(p *RunParams, res *repo.DatasetRef) error { + if r.cli != nil { + return r.cli.Call("QueryRequests.Run", p, res) + } + var ( store = r.repo.Store() transform *dataset.Transform @@ -237,6 +256,10 @@ type DatasetQueriesParams struct { } func (r *QueryRequests) DatasetQueries(p *DatasetQueriesParams, res *[]*repo.DatasetRef) error { + if r.cli != nil { + return r.cli.Call("QueryRequests.DatasetQueries", p, res) + } + if p.Path == "" { return fmt.Errorf("path is required") } diff --git a/core/search.go b/core/search.go index d35141984..b0e2f1c8a 100644 --- a/core/search.go +++ b/core/search.go @@ -2,6 +2,7 @@ package core import ( "fmt" + "net/rpc" "github.com/qri-io/cafs" "github.com/qri-io/qri/repo" @@ -12,18 +13,27 @@ type SearchRequests struct { store cafs.Filestore repo repo.Repo // node *p2p.QriNode + cli *rpc.Client } func (d SearchRequests) CoreRequestsName() string { return "search" } -func NewSearchRequests(r repo.Repo) *SearchRequests { +func NewSearchRequests(r repo.Repo, cli *rpc.Client) *SearchRequests { + if r != nil && cli != nil { + panic(fmt.Errorf("both repo and client supplied to NewSearchRequests")) + } + return &SearchRequests{ repo: r, // node: node, + cli: cli, } } func (d *SearchRequests) Search(p *repo.SearchParams, res *[]*repo.DatasetRef) error { + if d.cli != nil { + return d.cli.Call("SearchRequests.Search", p, res) + } // if d.node != nil { // r, err := d.node.Search(p.Query, p.Limit, p.Offset) // if err != nil { @@ -49,6 +59,10 @@ type ReindexSearchParams struct { } func (d *SearchRequests) Reindex(p *ReindexSearchParams, done *bool) error { + if d.cli != nil { + return d.cli.Call("SearchRequests.Reindex", p, done) + } + if fsr, ok := d.repo.(*fs_repo.Repo); ok { err := fsr.UpdateSearchIndex(d.repo.Store()) if err != nil {