From c2b56eae302ff929914f689705b020fe2483741f Mon Sep 17 00:00:00 2001 From: Umputun Date: Sun, 9 Jun 2019 01:22:35 -0500 Subject: [PATCH] remote client adjusted to new engine flavour --- backend/app/store/engine/engine.go | 39 +++++++------- backend/app/store/remote/remote.go | 66 ++++++++++++++++-------- backend/app/store/remote/remote_test.go | 68 +++++++++++++++++++------ 3 files changed, 118 insertions(+), 55 deletions(-) diff --git a/backend/app/store/engine/engine.go b/backend/app/store/engine/engine.go index 4e1688c3..cd618fe2 100644 --- a/backend/app/store/engine/engine.go +++ b/backend/app/store/engine/engine.go @@ -22,7 +22,7 @@ type Interface interface { Find(req FindRequest) ([]store.Comment, error) // find comments for locator or site Info(req InfoRequest) ([]store.PostInfo, error) // get post(s) meta info Count(req FindRequest) (int, error) // get count for post or user - Delete(req DeleteRequest) error // delete post(s) by id or by userID + Delete(req DeleteRequest) error // delete post(s) by id or by userID Flag(req FlagRequest) (bool, error) // set and get flags ListFlags(siteID string, flag Flag) ([]interface{}, error) // get list of flagged keys, like blocked & verified user Close() error // close storage engine @@ -30,25 +30,28 @@ type Interface interface { // FindRequest is the input for all find operations type FindRequest struct { - Locator store.Locator // lack of URL means site operation - UserID string // presence of UserID treated as user-related find - Sort string // sort order with +/-field syntax - Since time.Time // time limit for found results - Limit, Skip int + Locator store.Locator `json:"locator"` // lack of URL means site operation + UserID string `json:"user_id,omitempty"` // presence of UserID treated as user-related find + Sort string `json:"sort,omitempty"` // sort order with +/-field syntax + Since time.Time `json:"since,omitempty"` // time limit for found results + Limit int `json:"limit,omitempty"` + Skip int `json:"skip,omitempty"` } // InfoRequest is the input of Info operation used to get meta data about posts type InfoRequest struct { - Locator store.Locator - Limit, Skip int - ReadOnlyAge int + Locator store.Locator `json:"locator"` + Limit int `json:"limit,omitempty"` + Skip int `json:"skip,omitempty"` + ReadOnlyAge int `json:"ro_age,omitempty"` } +// DeleteRequest is the input for all delete operations (comments, sites, users) type DeleteRequest struct { - Locator store.Locator // lack of URL means site operation - CommentID string - UserID string - DeleteMode store.DeleteMode + Locator store.Locator `json:"locator"` // lack of URL means site operation + CommentID string `json:"comment_id,omitempty"` + UserID string `json:"user_id,omitempty"` + DeleteMode store.DeleteMode `json:"del_mode"` } // Flag defines type of binary attribute @@ -73,11 +76,11 @@ const ( // FlagRequest is the input for both get/set for flags, like blocked, verified and so on type FlagRequest struct { - Flag Flag // flag type - Locator store.Locator // post locator - UserID string // for flags setting user status - Update FlagStatus // if FlagNonSet it will be get op, if set will set the value - TTL time.Duration // ttl for time-sensitive flags only, like blocked for some period + Flag Flag `json:"flag"` // flag type + Locator store.Locator `json:"locator"` // post locator + UserID string `json:"user_id,omitempty"` // for flags setting user status + Update FlagStatus `json:"update,omitempty"` // if FlagNonSet it will be get op, if set will set the value + TTL time.Duration `json:"ttl,omitempty"` // ttl for time-sensitive flags only, like blocked for some period } const ( diff --git a/backend/app/store/remote/remote.go b/backend/app/store/remote/remote.go index ff805465..a39a5249 100644 --- a/backend/app/store/remote/remote.go +++ b/backend/app/store/remote/remote.go @@ -4,11 +4,11 @@ import ( "bytes" "encoding/json" "net/http" - "time" "github.com/pkg/errors" "github.com/umputun/remark/backend/app/store" + "github.com/umputun/remark/backend/app/store/engine" ) // Client implements remote engine and delegates all calls to remote http server @@ -54,45 +54,55 @@ func (r *Client) Get(locator store.Locator, commentID string) (comment store.Com return comment, err } -// Put updates comment, mutable parts only -func (r *Client) Put(locator store.Locator, comment store.Comment) error { - _, err := r.call("put", locator, comment) +// Update comment, mutable parts only +func (r *Client) Update(locator store.Locator, comment store.Comment) error { + _, err := r.call("update", locator, comment) return err } // Find comments for locator -func (r *Client) Find(locator store.Locator, sort string) (comments []store.Comment, err error) { - resp, err := r.call("find", locator, sort) +func (r *Client) Find(req engine.FindRequest) (comments []store.Comment, err error) { + resp, err := r.call("find", req) if err != nil { - return []store.Comment{}, err + return nil, err } err = json.Unmarshal(*resp.Result, &comments) return comments, err } -// Last comments for given site, sorted by time -func (r *Client) Last(siteID string, limit int, since time.Time) (comments []store.Comment, err error) { - resp, err := r.call("last", siteID, limit, since) +// Info returns post(s) meta info +func (r *Client) Info(req engine.InfoRequest) (info []store.PostInfo, err error) { + resp, err := r.call("info", req) if err != nil { - return []store.Comment{}, err + return nil, err } - err = json.Unmarshal(*resp.Result, &comments) - return comments, err + err = json.Unmarshal(*resp.Result, &info) + return info, err } -// User get comments by user, sorted by time -func (r *Client) User(siteID, userID string, limit, skip int) (comments []store.Comment, err error) { - resp, err := r.call("user", siteID, userID, limit, skip) +// Flag sets and gets flags +func (r *Client) Flag(req engine.FlagRequest) (status bool, err error) { + resp, err := r.call("flag", req) if err != nil { - return []store.Comment{}, err + return false, err } - err = json.Unmarshal(*resp.Result, &comments) - return comments, err + err = json.Unmarshal(*resp.Result, &status) + return status, err } -// UserCount gets comments count by user -func (r *Client) UserCount(siteID, userID string) (count int, err error) { - resp, err := r.call("user_count", siteID, userID) +// ListFlags get list of flagged keys, like blocked & verified user +func (r *Client) ListFlags(siteID string, flag engine.Flag) (list []interface{}, err error) { + resp, err := r.call("list_flags", siteID, flag) + if err != nil { + return nil, err + } + err = json.Unmarshal(*resp.Result, &list) + return list, err +} + +// Count gets comments count by user or site +func (r *Client) Count(req engine.FindRequest) (count int, err error) { + resp, err := r.call("count", req) if err != nil { return 0, err } @@ -100,6 +110,18 @@ func (r *Client) UserCount(siteID, userID string) (count int, err error) return count, err } +// Delete post(s) by id or by userID +func (r *Client) Delete(req engine.DeleteRequest) error { + _, err := r.call("delete", req) + return err +} + +// Close storage engine +func (r *Client) Close() error { + _, err := r.call("close") + return err +} + func (r *Client) call(method string, args ...interface{}) (*Response, error) { b, err := json.Marshal(Request{Method: method, Params: args}) diff --git a/backend/app/store/remote/remote_test.go b/backend/app/store/remote/remote_test.go index d3e56f9d..45814c3a 100644 --- a/backend/app/store/remote/remote_test.go +++ b/backend/app/store/remote/remote_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "github.com/umputun/remark/backend/app/store" + "github.com/umputun/remark/backend/app/store/engine" ) func TestClient_Create(t *testing.T) { @@ -79,47 +80,84 @@ func TestClient_FailedStatus(t *testing.T) { assert.EqualError(t, err, "bad status 400 for get") } -func TestClient_Put(t *testing.T) { - ts := testServer(t, `{"method":"put","params":[{"url":"http://example.com/url"},{"id":"123","pid":"","text":"msg","user":{"name":"","id":"","picture":"","admin":false},"locator":{"site":"site123","url":"http://example.com/url"},"score":0,"vote":0,"time":"0001-01-01T00:00:00Z"}]}`, `{}`) +func TestClient_Update(t *testing.T) { + ts := testServer(t, `{"method":"update","params":[{"url":"http://example.com/url"},{"id":"123","pid":"","text":"msg","user":{"name":"","id":"","picture":"","admin":false},"locator":{"site":"site123","url":"http://example.com/url"},"score":0,"vote":0,"time":"0001-01-01T00:00:00Z"}]}`, `{}`) defer ts.Close() c := Client{API: ts.URL, Client: http.Client{}} - err := c.Put(store.Locator{URL: "http://example.com/url"}, store.Comment{ID: "123", + err := c.Update(store.Locator{URL: "http://example.com/url"}, store.Comment{ID: "123", Locator: store.Locator{URL: "http://example.com/url", SiteID: "site123"}, Text: "msg"}) assert.NoError(t, err) } func TestClient_Find(t *testing.T) { - ts := testServer(t, `{"method":"find","params":[{"url":"http://example.com/url"},""]}`, - `{"result":[{"text":"1"},{"text":"2"}]}`) + ts := testServer(t, `{"method":"find","params":[{"locator":{"url":"http://example.com/url"},"sort":"-time","since":"0001-01-01T00:00:00Z","limit":10}]}`, `{"result":[{"text":"1"},{"text":"2"}]}`) defer ts.Close() c := Client{API: ts.URL, Client: http.Client{}} - res, err := c.Find(store.Locator{URL: "http://example.com/url"}, "") + res, err := c.Find(engine.FindRequest{Locator: store.Locator{URL: "http://example.com/url"}, Sort: "-time", Limit: 10}) assert.NoError(t, err) assert.Equal(t, []store.Comment{{Text: "1"}, {Text: "2"}}, res) } -func TestClient_Last(t *testing.T) { - ts := testServer(t, `{"method":"last","params":["site1",100,"2019-06-06T19:34:10Z"]}`, - `{"result":[{"text":"1"},{"text":"2"}]}`) +func TestClient_Info(t *testing.T) { + ts := testServer(t, `{"method":"info","params":[{"locator":{"url":"http://example.com/url"},"limit":10,"skip":5,"ro_age":10}]}`, `{"result":[{"url":"u1","count":22},{"url":"u2","count":33}]}`) defer ts.Close() c := Client{API: ts.URL, Client: http.Client{}} - res, err := c.Last("site1", 100, time.Date(2019, 6, 6, 19, 34, 10, 0, time.UTC)) + res, err := c.Info(engine.InfoRequest{Locator: store.Locator{URL: "http://example.com/url"}, + Limit: 10, Skip: 5, ReadOnlyAge: 10}) assert.NoError(t, err) - assert.Equal(t, []store.Comment{{Text: "1"}, {Text: "2"}}, res) + assert.Equal(t, []store.PostInfo{{URL: "u1", Count: 22}, {URL: "u2", Count: 33}}, res) } -func TestClient_User(t *testing.T) { - ts := testServer(t, `{"method":"user","params":["site1","u1",100,4]}`, `{"result":[{"text":"1"},{"text":"2"}]}`) +func TestClient_Flag(t *testing.T) { + ts := testServer(t, `{"method":"flag","params":[{"flag":"verified","locator":{"url":"http://example.com/url"}}]}`, + `{"result":false}`) defer ts.Close() c := Client{API: ts.URL, Client: http.Client{}} - res, err := c.User("site1", "u1", 100, 4) + res, err := c.Flag(engine.FlagRequest{Locator: store.Locator{URL: "http://example.com/url"}, Flag: engine.Verified}) + assert.NoError(t, err) + assert.Equal(t, false, res) +} + +func TestClient_ListFlag(t *testing.T) { + ts := testServer(t, `{"method":"list_flags","params":["site_id","blocked"]}`, `{"result":[{"ID":"id1"},{"ID":"id2"}]}`) + defer ts.Close() + c := Client{API: ts.URL, Client: http.Client{}} + res, err := c.ListFlags("site_id", engine.Blocked) + assert.NoError(t, err) + assert.Equal(t, []interface{}{map[string]interface{}{"ID": "id1"}, map[string]interface{}{"ID": "id2"}}, res) +} + +func TestClient_Count(t *testing.T) { + ts := testServer(t, `{"method":"count","params":[{"locator":{"url":"http://example.com/url"},"since":"0001-01-01T00:00:00Z"}]}`, + `{"result":11}`) + defer ts.Close() + c := Client{API: ts.URL, Client: http.Client{}} + + res, err := c.Count(engine.FindRequest{Locator: store.Locator{URL: "http://example.com/url"}}) + assert.NoError(t, err) + assert.Equal(t, 11, res) +} + +func TestClient_Delete(t *testing.T) { + ts := testServer(t, `{"method":"delete","params":[{"locator":{"url":"http://example.com/url"},"del_mode":0}]}`, `{}`) + defer ts.Close() + c := Client{API: ts.URL, Client: http.Client{}} + + err := c.Delete(engine.DeleteRequest{Locator: store.Locator{URL: "http://example.com/url"}}) + assert.NoError(t, err) +} + +func TestClient_Close(t *testing.T) { + ts := testServer(t, `{"method":"close","params":null}`, `{}`) + defer ts.Close() + c := Client{API: ts.URL, Client: http.Client{}} + err := c.Close() assert.NoError(t, err) - assert.Equal(t, []store.Comment{{Text: "1"}, {Text: "2"}}, res) } func testServer(t *testing.T, req, resp string) *httptest.Server {