Skip to content

Commit

Permalink
feat(httpClient): introducing an httpClient (#1629)
Browse files Browse the repository at this point in the history
  • Loading branch information
Arqu authored Feb 5, 2021
1 parent a41140a commit 8ecde53
Show file tree
Hide file tree
Showing 13 changed files with 475 additions and 71 deletions.
90 changes: 45 additions & 45 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,82 +151,82 @@ func NewServerRoutes(s Server) *http.ServeMux {
m = http.NewServeMux()
}

m.Handle("/", s.NoLogMiddleware(s.HomeHandler))
m.Handle("/health", s.NoLogMiddleware(HealthCheckHandler))
m.Handle("/ipfs/", s.Middleware(s.HandleIPFSPath))
m.Handle(lib.AEHome.String(), s.NoLogMiddleware(s.HomeHandler))
m.Handle(lib.AEHealth.String(), s.NoLogMiddleware(HealthCheckHandler))
m.Handle(lib.AEIPFS.String(), s.Middleware(s.HandleIPFSPath))

proh := NewProfileHandlers(s.Instance, cfg.API.ReadOnly)
m.Handle("/me", s.Middleware(proh.ProfileHandler))
m.Handle("/profile", s.Middleware(proh.ProfileHandler))
m.Handle("/profile/photo", s.Middleware(proh.ProfilePhotoHandler))
m.Handle("/profile/poster", s.Middleware(proh.PosterHandler))
m.Handle(lib.AEMe.String(), s.Middleware(proh.ProfileHandler))
m.Handle(lib.AEProfile.String(), s.Middleware(proh.ProfileHandler))
m.Handle(lib.AEProfilePhoto.String(), s.Middleware(proh.ProfilePhotoHandler))
m.Handle(lib.AEProfilePoster.String(), s.Middleware(proh.PosterHandler))

ph := NewPeerHandlers(s.Instance, cfg.API.ReadOnly)
m.Handle("/peers", s.Middleware(ph.PeersHandler))
m.Handle("/peers/", s.Middleware(ph.PeerHandler))
m.Handle("/connect/", s.Middleware(ph.ConnectToPeerHandler))
m.Handle("/connections", s.Middleware(ph.ConnectionsHandler))
m.Handle(lib.AEPeers.String(), s.Middleware(ph.PeersHandler))
m.Handle(lib.AEPeer.String(), s.Middleware(ph.PeerHandler))
m.Handle(lib.AEConnect.String(), s.Middleware(ph.ConnectToPeerHandler))
m.Handle(lib.AEConnections.String(), s.Middleware(ph.ConnectionsHandler))

if cfg.Remote != nil && cfg.Remote.Enabled {
log.Info("running in `remote` mode")

remh := NewRemoteHandlers(s.Instance)
m.Handle("/remote/dsync", s.Middleware(remh.DsyncHandler))
m.Handle("/remote/logsync", s.Middleware(remh.LogsyncHandler))
m.Handle("/remote/refs", s.Middleware(remh.RefsHandler))
m.Handle(lib.AERemoteDSync.String(), s.Middleware(remh.DsyncHandler))
m.Handle(lib.AERemoteLogSync.String(), s.Middleware(remh.LogsyncHandler))
m.Handle(lib.AERemoteRefs.String(), s.Middleware(remh.RefsHandler))
}

dsh := NewDatasetHandlers(s.Instance, cfg.API.ReadOnly)
m.Handle("/list", s.Middleware(dsh.ListHandler))
m.Handle("/list/", s.Middleware(dsh.PeerListHandler))
m.Handle("/save", s.Middleware(dsh.SaveHandler))
m.Handle("/save/", s.Middleware(dsh.SaveHandler))
m.Handle("/remove/", s.Middleware(dsh.RemoveHandler))
m.Handle("/get/", s.Middleware(dsh.GetHandler))
m.Handle("/rename", s.Middleware(dsh.RenameHandler))
m.Handle("/diff", s.Middleware(dsh.DiffHandler))
m.Handle("/changes", s.Middleware(dsh.ChangesHandler))
m.Handle(lib.AEList.String(), s.Middleware(dsh.ListHandler))
m.Handle(lib.AEPeerList.String(), s.Middleware(dsh.PeerListHandler))
m.Handle(lib.AESave.String(), s.Middleware(dsh.SaveHandler))
m.Handle(lib.AESaveAlt.String(), s.Middleware(dsh.SaveHandler))
m.Handle(lib.AERemove.String(), s.Middleware(dsh.RemoveHandler))
m.Handle(lib.AEGet.String(), s.Middleware(dsh.GetHandler))
m.Handle(lib.AERename.String(), s.Middleware(dsh.RenameHandler))
m.Handle(lib.AEDiff.String(), s.Middleware(dsh.DiffHandler))
m.Handle(lib.AEChanges.String(), s.Middleware(dsh.ChangesHandler))
// Deprecated, use /get/username/name?component=body or /get/username/name/body.csv
m.Handle("/body/", s.Middleware(dsh.BodyHandler))
m.Handle("/stats/", s.Middleware(dsh.StatsHandler))
m.Handle("/unpack/", s.Middleware(dsh.UnpackHandler))
m.Handle(lib.AEBody.String(), s.Middleware(dsh.BodyHandler))
m.Handle(lib.AEStats.String(), s.Middleware(dsh.StatsHandler))
m.Handle(lib.AEUnpack.String(), s.Middleware(dsh.UnpackHandler))

remClientH := NewRemoteClientHandlers(s.Instance, cfg.API.ReadOnly)
m.Handle("/push/", s.Middleware(remClientH.PushHandler))
m.Handle("/pull/", s.Middleware(dsh.PullHandler))
m.Handle("/feeds", s.Middleware(remClientH.FeedsHandler))
m.Handle("/preview/", s.Middleware(remClientH.DatasetPreviewHandler))
m.Handle(lib.AEPush.String(), s.Middleware(remClientH.PushHandler))
m.Handle(lib.AEPull.String(), s.Middleware(dsh.PullHandler))
m.Handle(lib.AEFeeds.String(), s.Middleware(remClientH.FeedsHandler))
m.Handle(lib.AEPreview.String(), s.Middleware(remClientH.DatasetPreviewHandler))

fsih := NewFSIHandlers(s.Instance, cfg.API.ReadOnly)
m.Handle("/status/", s.Middleware(fsih.StatusHandler("/status")))
m.Handle("/whatchanged/", s.Middleware(fsih.WhatChangedHandler("/whatchanged")))
m.Handle("/init/", s.Middleware(fsih.InitHandler("/init")))
m.Handle("/checkout/", s.Middleware(fsih.CheckoutHandler("/checkout")))
m.Handle("/restore/", s.Middleware(fsih.RestoreHandler("/restore")))
m.Handle("/fsi/write/", s.Middleware(fsih.WriteHandler("/fsi/write")))
m.Handle(lib.AEStatus.String(), s.Middleware(fsih.StatusHandler(lib.AEStatus.NoTrailingSlash())))
m.Handle(lib.AEWhatChanged.String(), s.Middleware(fsih.WhatChangedHandler(lib.AEWhatChanged.NoTrailingSlash())))
m.Handle(lib.AEInit.String(), s.Middleware(fsih.InitHandler(lib.AEInit.NoTrailingSlash())))
m.Handle(lib.AECheckout.String(), s.Middleware(fsih.CheckoutHandler(lib.AECheckout.NoTrailingSlash())))
m.Handle(lib.AERestore.String(), s.Middleware(fsih.RestoreHandler(lib.AERestore.NoTrailingSlash())))
m.Handle(lib.AEFSIWrite.String(), s.Middleware(fsih.WriteHandler(lib.AEFSIWrite.NoTrailingSlash())))

renderh := NewRenderHandlers(s.Instance)
m.Handle("/render", s.Middleware(renderh.RenderHandler))
m.Handle("/render/", s.Middleware(renderh.RenderHandler))
m.Handle(lib.AERender.String(), s.Middleware(renderh.RenderHandler))
m.Handle(lib.AERenderAlt.String(), s.Middleware(renderh.RenderHandler))

lh := NewLogHandlers(s.Instance)
m.Handle("/history/", s.Middleware(lh.LogHandler))
m.Handle(lib.AEHistory.String(), s.Middleware(lh.LogHandler))

rch := NewRegistryClientHandlers(s.Instance, cfg.API.ReadOnly)
m.Handle("/registry/profile/new", s.Middleware(rch.CreateProfileHandler))
m.Handle("/registry/profile/prove", s.Middleware(rch.ProveProfileKeyHandler))
m.Handle(lib.AERegistryNew.String(), s.Middleware(rch.CreateProfileHandler))
m.Handle(lib.AERegistryProve.String(), s.Middleware(rch.ProveProfileKeyHandler))

sh := NewSearchHandlers(s.Instance)
m.Handle("/search", s.Middleware(sh.SearchHandler))
m.Handle(lib.AESearch.String(), s.Middleware(sh.SearchHandler))

sqlh := NewSQLHandlers(s.Instance, cfg.API.ReadOnly)
m.Handle("/sql", s.Middleware(sqlh.QueryHandler("/sql")))
m.Handle(lib.AESQL.String(), s.Middleware(sqlh.QueryHandler("/sql")))

tfh := NewTransformHandlers(s.Instance)
m.Handle("/apply", s.Middleware(tfh.ApplyHandler("/apply")))
m.Handle(lib.AEApply.String(), s.Middleware(tfh.ApplyHandler(lib.AEApply.NoTrailingSlash())))

if !cfg.API.DisableWebui {
m.Handle("/webui", s.Middleware(WebuiHandler))
m.Handle(lib.AEWebUI.String(), s.Middleware(WebuiHandler))
}

return m
Expand Down
2 changes: 1 addition & 1 deletion api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
"github.com/beme/abide"
golog "github.com/ipfs/go-log"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr-net"
manet "github.com/multiformats/go-multiaddr/net"
"github.com/qri-io/qri/base/dsfs"
"github.com/qri-io/qri/config"
"github.com/qri-io/qri/event"
Expand Down
103 changes: 103 additions & 0 deletions api/http_client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package api

import (
"context"
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"

"github.com/google/go-cmp/cmp"
golog "github.com/ipfs/go-log"
"github.com/qri-io/qri/base/dsfs"
"github.com/qri-io/qri/config"
"github.com/qri-io/qri/dsref"
"github.com/qri-io/qri/event"
"github.com/qri-io/qri/lib"
"github.com/qri-io/qri/p2p"
"github.com/qri-io/qri/repo/test"
)

func TestHTTPClient(t *testing.T) {
if err := confirmQriNotRunning(); err != nil {
t.Skip(err.Error())
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

prevXformVer := APIVersion
APIVersion = "test_version"
defer func() {
APIVersion = prevXformVer
}()

// bump up log level to keep test output clean
golog.SetLogLevel("qriapi", "error")
defer golog.SetLogLevel("qriapi", "info")

// to keep hashes consistent, artificially specify the timestamp by overriding
// the dsfs.Timestamp func
prev := dsfs.Timestamp
defer func() { dsfs.Timestamp = prev }()
dsfs.Timestamp = func() time.Time { return time.Date(2001, 01, 01, 01, 01, 01, 01, time.UTC) }

r, err := test.NewTestRepo()
if err != nil {
t.Fatalf("error allocating test repo: %s", err.Error())
}

// Cannot use TestRunner because we need to set cfg.API.ReadOnly.
// TODO(dlong): Add a testRunner call trace that does this correctly.
cfg := config.DefaultConfigForTesting()

node, err := p2p.NewQriNode(r, cfg.P2P, event.NilBus, nil)
if err != nil {
t.Fatal(err.Error())
}
// TODO (b5) - hack until tests have better instance-generation primitives
inst := lib.NewInstanceFromConfigAndNode(ctx, cfg, node)
s := New(inst)

server := httptest.NewServer(NewServerRoutes(s))
sURL, err := url.Parse(server.URL)
if err != nil {
t.Fatal(err.Error())
}

httpClient, err := lib.NewHTTPClient(cfg.API.Address)
if err != nil {
t.Fatal(err.Error())
}

// override with test URI
httpClient.Address = sURL.Host
httpClient.Protocol = "http"

err = httpClient.Call(ctx, lib.AEHome, nil, &map[string]interface{}{})
if err != nil {
t.Fatal(err.Error())
}

res := []dsref.VersionInfo{}
err = httpClient.CallMethod(ctx, lib.AEList, http.MethodGet, nil, &res)
if err != nil {
t.Fatal(err.Error())
}

body, err := json.Marshal(res)
if err != nil {
t.Fatal(err.Error())
}
// Compare the API response to the expected zip file
expectBytes, err := ioutil.ReadFile("testdata/http_client/list.json")
if err != nil {
t.Fatalf("error reading expected bytes: %s", err)
}
if diff := cmp.Diff(string(expectBytes), string(body)); diff != "" {
t.Errorf("byte mismatch (-want +got):\n%s", diff)
}
}
Binary file modified api/testdata/api.snapshot
Binary file not shown.
1 change: 1 addition & 0 deletions api/testdata/http_client/list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"username":"peer","profileID":"QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt","name":"cities","path":"/mem/QmWMa5BokbgXoq4AEv7HQXDPkNzqbb8EA9x2WrpWhafvns","metaTitle":"example city data","bodySize":155,"bodyRows":5,"bodyFormat":"csv","commitTime":"2001-01-01T01:01:01.000000001Z"},{"username":"peer","profileID":"QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt","name":"counter","path":"/mem/QmZ6YJEyYZMwNVsei5tjvvz8KEx5ru7QpT9NKLAHWJLKEt","metaTitle":"it's a counter","bodySize":56,"bodyRows":20,"bodyFormat":"csv","commitTime":"2001-01-01T01:01:01.000000001Z"},{"username":"peer","profileID":"QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt","name":"craigslist","path":"/mem/QmcNrAVWyo7N1idPMxyiEw4pLGwem3tMes9XspVo3Lnf4b","bodySize":18308,"bodyRows":5,"bodyFormat":"json","commitTime":"2001-01-01T01:01:01.000000001Z"},{"username":"peer","profileID":"QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt","name":"movies","path":"/mem/QmQPS7Nf6dG8zosyAA8zYd64gaLBTAzYsVhMkaMCgCXJST","metaTitle":"example movie data","bodySize":49753,"bodyRows":2335,"bodyFormat":"csv","numErrors":4,"commitTime":"2001-01-01T01:01:01.000000001Z"},{"username":"peer","profileID":"QmZePf5LeXow3RW5U1AgEiNbW46YnRGhZ7HPvm1UmPFPwt","name":"sitemap","path":"/mem/QmfW651kmdLQpyEK9o4TJ8hxoTjnVSLpak6kDsrmB3zuRH","metaTitle":"epa.gov sitemap entry sample","bodySize":12556,"bodyRows":11,"bodyFormat":"json","commitTime":"2001-01-01T01:01:01.000000001Z"}]
2 changes: 1 addition & 1 deletion api/transports.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"time"

ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr-net"
manet "github.com/multiformats/go-multiaddr/net"
"github.com/qri-io/qri/config"
)

Expand Down
51 changes: 33 additions & 18 deletions api/util/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,27 @@ import (
"net/http"
)

// Response is the JSON API response object wrapper
type Response struct {
Data interface{} `json:"data,omitempty"`
Meta *Meta `json:"meta,omitempty"`
Pagination *Page `json:"pagination,omitempty"`
}

// Meta is the JSON API response meta object wrapper
type Meta struct {
Code int `json:"code,omitempty"`
Message string `json:"message,omitempty"`
Error string `json:"error,omitempty"`
}

// WriteResponse wraps response data in an envelope & writes it
func WriteResponse(w http.ResponseWriter, data interface{}) error {
env := map[string]interface{}{
"meta": map[string]interface{}{
"code": http.StatusOK,
env := Response{
Meta: &Meta{
Code: http.StatusOK,
},
"data": data,
Data: data,
}
return jsonResponse(w, env)
}
Expand All @@ -26,35 +40,36 @@ func WritePageResponse(w http.ResponseWriter, data interface{}, r *http.Request,
p.NextURL = p.Next().SetQueryParams(r.URL).String()
}

env := map[string]interface{}{
"meta": map[string]interface{}{
"code": http.StatusOK,
env := Response{
Meta: &Meta{
Code: http.StatusOK,
},
"data": data,
"pagination": p,
Data: data,
Pagination: &p,
}

return jsonResponse(w, env)
}

// WriteMessageResponse includes a message with a data response
func WriteMessageResponse(w http.ResponseWriter, message string, data interface{}) error {
env := map[string]interface{}{
"meta": map[string]interface{}{
"code": http.StatusOK,
"message": message,
env := Response{
Meta: &Meta{
Code: http.StatusOK,
Message: message,
},
"data": data,
Data: data,
}

return jsonResponse(w, env)
}

// WriteErrResponse writes a JSON error response message & HTTP status
func WriteErrResponse(w http.ResponseWriter, code int, err error) error {
env := map[string]interface{}{
"meta": map[string]interface{}{
"code": code,
"error": err.Error(),
env := Response{
Meta: &Meta{
Code: code,
Error: err.Error(),
},
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/google/go-cmp/cmp"
golog "github.com/ipfs/go-log"
ma "github.com/multiformats/go-multiaddr"
manet "github.com/multiformats/go-multiaddr-net"
manet "github.com/multiformats/go-multiaddr/net"
"github.com/qri-io/dataset"
"github.com/qri-io/dataset/dstest"
"github.com/qri-io/qfs"
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ require (
github.com/microcosm-cc/bluemonday v1.0.2
github.com/mitchellh/go-homedir v1.1.0
github.com/mr-tron/base58 v1.2.0
github.com/multiformats/go-multiaddr v0.3.1
github.com/multiformats/go-multiaddr v0.3.2-0.20210122024440-7274874c78df
github.com/multiformats/go-multiaddr-net v0.2.0
github.com/multiformats/go-multicodec v0.1.6
github.com/multiformats/go-multihash v0.0.14
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,8 @@ github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u
github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI=
github.com/multiformats/go-multiaddr v0.3.1 h1:1bxa+W7j9wZKTZREySx1vPMs2TqrYWjVZ7zE6/XLG1I=
github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc=
github.com/multiformats/go-multiaddr v0.3.2-0.20210122024440-7274874c78df h1:4y6jJdqKIPS07xHjIFu/YAJNYi4XYLESBB5LhOUnjss=
github.com/multiformats/go-multiaddr v0.3.2-0.20210122024440-7274874c78df/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc=
github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
github.com/multiformats/go-multiaddr-dns v0.0.3/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q=
Expand Down Expand Up @@ -1125,8 +1127,6 @@ github.com/qri-io/compare v0.1.0 h1:A/MRx3uEnJ/iMjfJY1VOqH9CYs9zFSEYaFVeXuGfmis=
github.com/qri-io/compare v0.1.0/go.mod h1:i/tVuDGRXVxhuZ8ZUieF23u6rQ6wLGJl7KKWpoMRaTE=
github.com/qri-io/dag v0.2.2-0.20201208212257-ae00241c4b48 h1:6fTW2iHGbaEKQt9u8+04kB3m33KSGLqxF/2pWNleeEg=
github.com/qri-io/dag v0.2.2-0.20201208212257-ae00241c4b48/go.mod h1:1AwOy3yhcZTAXzaF4wGSdnrp87u3PBOrsWXUjOtQCXo=
github.com/qri-io/dataset v0.2.1-0.20210126031523-f94fd2290107 h1:K5cgpsL+r8kz7XmV01b53KrdwWcdcuL5nrfMzrAYNoU=
github.com/qri-io/dataset v0.2.1-0.20210126031523-f94fd2290107/go.mod h1:vlq9+Nu37koO3mrp25QGNOt68CLe2d2rAtB9cnDLV6E=
github.com/qri-io/dataset v0.2.1-0.20210128201320-3b1209495e96 h1:SiP48nzhKLJbvM6SA+5wK53PKUs0FY0DWDylMPyi8S4=
github.com/qri-io/dataset v0.2.1-0.20210128201320-3b1209495e96/go.mod h1:vlq9+Nu37koO3mrp25QGNOt68CLe2d2rAtB9cnDLV6E=
github.com/qri-io/deepdiff v0.2.1-0.20200807143746-d02d9f531f5b h1:T8qEIv+qLi5mVWvSS329wJ+HbN7cfMwCWjRVzh/+upo=
Expand Down
Loading

0 comments on commit 8ecde53

Please sign in to comment.