add OnEvent event to admin on each update/create/vote/delete

This commit is contained in:
Umputun
2019-08-26 20:53:04 -05:00
parent e9414f7267
commit 68235164ff
8 changed files with 110 additions and 7 deletions
@@ -9,6 +9,7 @@ package accessor
import (
log "github.com/go-pkgz/lgr"
"github.com/pkg/errors"
"github.com/umputun/remark/backend/app/store/admin"
)
// MemAdmin implements admin.Store with memory backend
@@ -19,10 +20,11 @@ type MemAdmin struct {
// AdminRec is a records per site with all admin info in
type AdminRec struct {
SiteID string
IDs []string // admin ids
Email string // admin email
Enabled bool // site enabled
SiteID string
IDs []string // admin ids
Email string // admin email
Enabled bool // site enabled
CountCreated int64 // number of created posts
}
// NewMemAdminStore makes admin Store in memory
@@ -65,6 +67,19 @@ func (m *MemAdmin) Enabled(siteID string) (ok bool, err error) {
return resp.Enabled, nil
}
// OnEvent reacts on events from updates, created, delete and vote
func (m *MemAdmin) OnEvent(siteID string, ev admin.EventType) error {
resp, ok := m.data[siteID]
if !ok {
return errors.Errorf("site %s not found", siteID)
}
if ev == admin.EvCreate {
resp.CountCreated += 1 // not a good idea, just for demo
m.data[siteID] = resp
}
return nil
}
// Set admin data for siteID
func (m *MemAdmin) Set(siteID string, arec AdminRec) {
m.data[siteID] = arec
@@ -20,9 +20,9 @@ func TestMemAdmin_Get(t *testing.T) {
var ms admin.Store = adm
adm.data = map[string]AdminRec{
"site1": {"site1", []string{"i11", "i12"}, "e1", true},
"site2": {"site2", []string{"i21", "i22"}, "e2", true},
"site3": {"site3", []string{"i21", "i22"}, "e3", false},
"site1": {"site1", []string{"i11", "i12"}, "e1", true, 0},
"site2": {"site2", []string{"i21", "i22"}, "e2", true, 0},
"site3": {"site3", []string{"i21", "i22"}, "e3", false, 0},
}
admins, err := ms.Admins("site1")
@@ -46,6 +46,7 @@ func (s *RPC) addHandlers() {
"admins": s.admAdminsHndl,
"email": s.admEmailHndl,
"enabled": s.admEnabledHndl,
"event": s.admEventHndl,
})
}
@@ -196,3 +197,25 @@ func (s *RPC) admEnabledHndl(id uint64, params json.RawMessage) (rr jrpc.Respons
}
return jrpc.EncodeResponse(id, ok, err)
}
// onEvent returns nothing, callback to OnEvent
func (s *RPC) admEventHndl(id uint64, params json.RawMessage) (rr jrpc.Response) {
var siteID string
ps := []interface{}{}
if err := json.Unmarshal(params, &ps); err != nil {
return jrpc.Response{Error: err.Error()}
}
siteID, ok := ps[0].(string)
if !ok {
return jrpc.Response{Error: "wrong siteID type"}
}
evType, ok := ps[1].(float64)
if !ok {
return jrpc.Response{Error: "wrong event type"}
}
err := s.adm.OnEvent(siteID, admin.EventType(evType))
if err != nil {
return jrpc.Response{Error: err.Error()}
}
return jrpc.EncodeResponse(id, nil, err)
}
@@ -311,6 +311,20 @@ func TestRPC_admEnabledHndl(t *testing.T) {
assert.Equal(t, false, ok)
}
func TestRPC_admEventHndl(t *testing.T) {
_, port, teardown := prepTestStore(t)
defer teardown()
api := fmt.Sprintf("http://localhost:%d/test", port)
ra := admin.RPC{Client: jrpc.Client{API: api, Client: http.Client{Timeout: 1 * time.Second}}}
err := ra.OnEvent("bad site", admin.EvCreate)
assert.EqualError(t, err, "site bad site not found")
err = ra.OnEvent("test-site", admin.EvCreate)
assert.NoError(t, err)
}
func prepTestStore(t *testing.T) (s *RPC, port int, teardown func()) {
port = 40000 + int(rand.Int31n(10000))
+15
View File
@@ -14,8 +14,20 @@ type Store interface {
Admins(siteID string) (ids []string, err error)
Email(siteID string) (email string, err error)
Enabled(siteID string) (ok bool, err error)
OnEvent(siteID string, et EventType) error
}
// EventType indicates type of the event
type EventType int
// enum of all event types
const (
EvCreate EventType = iota
EvDelete
EvUpdate
EvVote
)
// StaticStore implements keys.Store with a single set of admins and email for all sites
type StaticStore struct {
admins []string
@@ -65,3 +77,6 @@ func (s *StaticStore) Enabled(site string) (ok bool, err error) {
}
return false, nil
}
// OnEvent doesn nothing for StaticStore
func (s *StaticStore) OnEvent(siteID string, et EventType) error { return nil }
+9
View File
@@ -66,3 +66,12 @@ func (r *RPC) Enabled(siteID string) (ok bool, err error) {
}
return ok, nil
}
// OnEvent reacts (register) events about data modification
func (r *RPC) OnEvent(siteID string, et EventType) error {
_, err := r.Call("admin.event", siteID, et)
if err != nil {
return err
}
return nil
}
+12
View File
@@ -78,6 +78,18 @@ func TestRemote_Enables(t *testing.T) {
t.Logf("%v %T", res, res)
}
func TestRemote_OnEvent(t *testing.T) {
ts := testServer(t, `{"method":"admin.event","params":["site-1",2],"id":1}`, `{"id":1}`)
defer ts.Close()
c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
var a Store = &c
_ = a
err := c.OnEvent("site-1", EvUpdate)
assert.NoError(t, err)
}
func testServer(t *testing.T, req, resp string) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
+15
View File
@@ -102,6 +102,10 @@ func (s *DataStore) Create(comment store.Comment) (commentID string, err error)
}()
s.submitImages(comment)
if e := s.AdminStore.OnEvent(comment.Locator.SiteID, admin.EvCreate); e != nil {
log.Printf("[WARN] failed to send create event, %s", e)
}
return s.Engine.Create(comment)
}
@@ -306,6 +310,10 @@ func (s *DataStore) Vote(req VoteReq) (comment store.Comment, err error) {
}
}
if e := s.AdminStore.OnEvent(comment.Locator.SiteID, admin.EvVote); e != nil {
log.Printf("[WARN] failed to send vote event, %s", e)
}
comment.Controversy = s.controversy(s.upsAndDowns(comment))
comment.Locator = req.Locator
return comment, s.Engine.Update(comment)
@@ -386,6 +394,10 @@ func (s *DataStore) EditComment(locator store.Locator, commentID string, req Edi
comment.Locator = locator
comment.Sanitize()
if e := s.AdminStore.OnEvent(comment.Locator.SiteID, admin.EvUpdate); e != nil {
log.Printf("[WARN] failed to send update event, %s", e)
}
err = s.Engine.Update(comment)
return comment, err
}
@@ -608,6 +620,9 @@ func (s *DataStore) Info(locator store.Locator, readonlyAge int) (store.PostInfo
// Delete comment by id
func (s *DataStore) Delete(locator store.Locator, commentID string, mode store.DeleteMode) error {
if e := s.AdminStore.OnEvent(locator.SiteID, admin.EvDelete); e != nil {
log.Printf("[WARN] failed to send delete event, %s", e)
}
req := engine.DeleteRequest{Locator: locator, CommentID: commentID, DeleteMode: mode}
return s.Engine.Delete(req)
}