Merge pull request #480 from paskal/email_interface_clarification
Switch notify.Submit to receive notify.Request instead of store.Comment
This commit is contained in:
@@ -16,7 +16,7 @@ import (
|
||||
type Service struct {
|
||||
dataService Store
|
||||
destinations []Destination
|
||||
queue chan request
|
||||
queue chan Request
|
||||
|
||||
closed uint32 // non-zero means closed. uses uint instead of bool for atomic
|
||||
ctx context.Context
|
||||
@@ -26,7 +26,7 @@ type Service struct {
|
||||
// Destination defines interface for a given destination service, like telegram, email and so on
|
||||
type Destination interface {
|
||||
fmt.Stringer
|
||||
Send(ctx context.Context, req request) error
|
||||
Send(ctx context.Context, req Request) error
|
||||
}
|
||||
|
||||
// Store defines the minimal interface accessing stored comments used by notifier
|
||||
@@ -34,8 +34,8 @@ type Store interface {
|
||||
Get(locator store.Locator, id string, user store.User) (store.Comment, error)
|
||||
}
|
||||
|
||||
type request struct {
|
||||
comment store.Comment
|
||||
type Request struct {
|
||||
Comment store.Comment
|
||||
parent store.Comment
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func NewService(dataService Store, size int, destinations ...Destination) *Servi
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
res := Service{
|
||||
dataService: dataService,
|
||||
queue: make(chan request, size),
|
||||
queue: make(chan Request, size),
|
||||
destinations: destinations,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
@@ -62,21 +62,20 @@ func NewService(dataService Store, size int, destinations ...Destination) *Servi
|
||||
return &res
|
||||
}
|
||||
|
||||
// Submit comment to internal channel if not busy, drop if can't send
|
||||
func (s *Service) Submit(comment store.Comment) {
|
||||
// Submit Request to internal channel if not busy, drop if can't send
|
||||
func (s *Service) Submit(req Request) {
|
||||
if len(s.destinations) == 0 || atomic.LoadUint32(&s.closed) != 0 {
|
||||
return
|
||||
}
|
||||
parentComment := store.Comment{}
|
||||
if s.dataService != nil {
|
||||
if p, err := s.dataService.Get(comment.Locator, comment.ParentID, store.User{}); err == nil {
|
||||
parentComment = p
|
||||
if p, err := s.dataService.Get(req.Comment.Locator, req.Comment.ParentID, store.User{}); err == nil {
|
||||
req.parent = p
|
||||
}
|
||||
}
|
||||
select {
|
||||
case s.queue <- request{comment: comment, parent: parentComment}:
|
||||
case s.queue <- req:
|
||||
default:
|
||||
log.Printf("[WARN] can't send comment notification to queue, %+v", comment)
|
||||
log.Printf("[WARN] can't send comment notification to queue, %+v", req.Comment)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ import (
|
||||
func TestService_NoDestinations(t *testing.T) {
|
||||
s := NewService(nil, 1)
|
||||
assert.NotNil(t, s)
|
||||
s.Submit(store.Comment{ID: "123"})
|
||||
s.Submit(store.Comment{ID: "123"})
|
||||
s.Submit(store.Comment{ID: "123"})
|
||||
s.Submit(Request{Comment: store.Comment{ID: "123"}})
|
||||
s.Submit(Request{Comment: store.Comment{ID: "123"}})
|
||||
s.Submit(Request{Comment: store.Comment{ID: "123"}})
|
||||
s.Close()
|
||||
}
|
||||
|
||||
@@ -30,20 +30,20 @@ func TestService_WithDestinations(t *testing.T) {
|
||||
s := NewService(nil, 1, d1, d2)
|
||||
assert.NotNil(t, s)
|
||||
|
||||
s.Submit(store.Comment{ID: "100"})
|
||||
s.Submit(Request{Comment: store.Comment{ID: "100"}})
|
||||
time.Sleep(time.Millisecond * 110)
|
||||
s.Submit(store.Comment{ID: "101"})
|
||||
s.Submit(Request{Comment: store.Comment{ID: "101"}})
|
||||
time.Sleep(time.Millisecond * 110)
|
||||
s.Submit(store.Comment{ID: "102"})
|
||||
s.Submit(Request{Comment: store.Comment{ID: "102"}})
|
||||
time.Sleep(time.Millisecond * 110)
|
||||
s.Close()
|
||||
|
||||
assert.Equal(t, 3, len(d1.get()), "got all comments to d1")
|
||||
assert.Equal(t, 3, len(d2.get()), "got all comments to d2")
|
||||
|
||||
assert.Equal(t, "100", d1.get()[0].comment.ID)
|
||||
assert.Equal(t, "101", d1.get()[1].comment.ID)
|
||||
assert.Equal(t, "102", d1.get()[2].comment.ID)
|
||||
assert.Equal(t, "100", d1.get()[0].Comment.ID)
|
||||
assert.Equal(t, "101", d1.get()[1].Comment.ID)
|
||||
assert.Equal(t, "102", d1.get()[2].Comment.ID)
|
||||
}
|
||||
|
||||
func TestService_WithDrops(t *testing.T) {
|
||||
@@ -51,14 +51,14 @@ func TestService_WithDrops(t *testing.T) {
|
||||
s := NewService(nil, 1, d1, d2)
|
||||
assert.NotNil(t, s)
|
||||
|
||||
s.Submit(store.Comment{ID: "100"})
|
||||
s.Submit(store.Comment{ID: "101"})
|
||||
s.Submit(Request{Comment: store.Comment{ID: "100"}})
|
||||
s.Submit(Request{Comment: store.Comment{ID: "101"}})
|
||||
time.Sleep(time.Millisecond * 110)
|
||||
s.Submit(store.Comment{ID: "102"})
|
||||
s.Submit(Request{Comment: store.Comment{ID: "102"}})
|
||||
time.Sleep(time.Millisecond * 110)
|
||||
s.Close()
|
||||
|
||||
s.Submit(store.Comment{ID: "111"}) // safe to send after close
|
||||
s.Submit(Request{Comment: store.Comment{ID: "111"}}) // safe to send after close
|
||||
|
||||
assert.Equal(t, 2, len(d1.get()), "one comment dropped from d1")
|
||||
assert.Equal(t, 2, len(d2.get()), "one comment dropped from d2")
|
||||
@@ -70,7 +70,7 @@ func TestService_Many(t *testing.T) {
|
||||
assert.NotNil(t, s)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
s.Submit(store.Comment{ID: fmt.Sprintf("%d", 100+i)})
|
||||
s.Submit(Request{Comment: store.Comment{ID: fmt.Sprintf("%d", 100+i)}})
|
||||
time.Sleep(time.Millisecond * time.Duration(rand.Int31n(200)))
|
||||
}
|
||||
s.Close()
|
||||
@@ -93,41 +93,41 @@ func TestService_WithParent(t *testing.T) {
|
||||
s := NewService(dataStore, 1, dest)
|
||||
assert.NotNil(t, s)
|
||||
|
||||
s.Submit(store.Comment{ID: "c1", ParentID: "p1"})
|
||||
s.Submit(Request{Comment: store.Comment{ID: "c1", ParentID: "p1"}})
|
||||
time.Sleep(time.Millisecond * 110)
|
||||
s.Submit(store.Comment{ID: "c11", ParentID: "p11"})
|
||||
s.Submit(Request{Comment: store.Comment{ID: "c11", ParentID: "p11"}})
|
||||
time.Sleep(time.Millisecond * 110)
|
||||
s.Close()
|
||||
|
||||
destRes := dest.get()
|
||||
assert.Equal(t, 2, len(destRes), "two comment notified")
|
||||
assert.Equal(t, "p1", destRes[0].comment.ParentID)
|
||||
assert.Equal(t, "p1", destRes[0].Comment.ParentID)
|
||||
assert.Equal(t, "p1", destRes[0].parent.ID)
|
||||
assert.Equal(t, "p11", destRes[1].comment.ParentID)
|
||||
assert.Equal(t, "p11", destRes[1].Comment.ParentID)
|
||||
assert.Equal(t, "", destRes[1].parent.ID)
|
||||
}
|
||||
|
||||
func TestService_Nop(t *testing.T) {
|
||||
s := NopService
|
||||
s.Submit(store.Comment{})
|
||||
s.Submit(Request{Comment: store.Comment{}})
|
||||
s.Close()
|
||||
assert.Equal(t, uint32(1), atomic.LoadUint32(&s.closed))
|
||||
}
|
||||
|
||||
type mockDest struct {
|
||||
data []request
|
||||
data []Request
|
||||
id int
|
||||
closed bool
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (m *mockDest) Send(ctx context.Context, r request) error {
|
||||
func (m *mockDest) Send(ctx context.Context, r Request) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
m.data = append(m.data, r)
|
||||
log.Printf("sent %s -> %d", r.comment.ID, m.id)
|
||||
log.Printf("sent %s -> %d", r.Comment.ID, m.id)
|
||||
case <-ctx.Done():
|
||||
log.Printf("ctx closed %d", m.id)
|
||||
m.closed = true
|
||||
@@ -135,10 +135,10 @@ func (m *mockDest) Send(ctx context.Context, r request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockDest) get() []request {
|
||||
func (m *mockDest) get() []Request {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
res := make([]request, len(m.data))
|
||||
res := make([]Request, len(m.data))
|
||||
copy(res, m.data)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -85,23 +85,23 @@ func NewTelegram(token string, channelID string, timeout time.Duration, api stri
|
||||
}
|
||||
|
||||
// Send to telegram channel
|
||||
func (t *Telegram) Send(ctx context.Context, req request) error {
|
||||
func (t *Telegram) Send(ctx context.Context, req Request) error {
|
||||
client := http.Client{Timeout: telegramTimeOut}
|
||||
log.Printf("[DEBUG] send telegram notification to %s, comment id %s", t.channelID, req.comment.ID)
|
||||
log.Printf("[DEBUG] send telegram notification to %s, comment id %s", t.channelID, req.Comment.ID)
|
||||
|
||||
from := req.comment.User.Name
|
||||
if req.comment.ParentID != "" {
|
||||
from := req.Comment.User.Name
|
||||
if req.Comment.ParentID != "" {
|
||||
from += " → " + req.parent.User.Name
|
||||
}
|
||||
from = "*" + from + "*"
|
||||
link := fmt.Sprintf("↦ [original comment](%s)", req.comment.Locator.URL+uiNav+req.comment.ID)
|
||||
if req.comment.PostTitle != "" {
|
||||
link = fmt.Sprintf("↦ [%s](%s)", req.comment.PostTitle, req.comment.Locator.URL+uiNav+req.comment.ID)
|
||||
link := fmt.Sprintf("↦ [original comment](%s)", req.Comment.Locator.URL+uiNav+req.Comment.ID)
|
||||
if req.Comment.PostTitle != "" {
|
||||
link = fmt.Sprintf("↦ [%s](%s)", req.Comment.PostTitle, req.Comment.Locator.URL+uiNav+req.Comment.ID)
|
||||
}
|
||||
u := fmt.Sprintf("%s%s/sendMessage?chat_id=%s&parse_mode=Markdown&disable_web_page_preview=true",
|
||||
t.apiPrefix, t.token, t.channelID)
|
||||
|
||||
msg := fmt.Sprintf("%s\n\n%s\n\n%s", from, req.comment.Orig, link)
|
||||
msg := fmt.Sprintf("%s\n\n%s\n\n%s", from, req.Comment.Orig, link)
|
||||
msg = html.UnescapeString(msg)
|
||||
body := struct {
|
||||
Text string `json:"text"`
|
||||
|
||||
@@ -63,15 +63,15 @@ func TestTelegram_Send(t *testing.T) {
|
||||
cp := store.Comment{Text: "some parent text"}
|
||||
cp.User.Name = "to"
|
||||
|
||||
err = tb.Send(context.TODO(), request{comment: c, parent: cp})
|
||||
err = tb.Send(context.TODO(), Request{Comment: c, parent: cp})
|
||||
assert.NoError(t, err)
|
||||
c.PostTitle = "test title"
|
||||
err = tb.Send(context.TODO(), request{comment: c, parent: cp})
|
||||
err = tb.Send(context.TODO(), Request{Comment: c, parent: cp})
|
||||
assert.NoError(t, err)
|
||||
|
||||
tb, err = NewTelegram("non-json-resp", "remark_test", 2*time.Second, ts.URL+"/")
|
||||
assert.NotNil(t, err, "should failed")
|
||||
err = tb.Send(context.TODO(), request{comment: c, parent: cp})
|
||||
err = tb.Send(context.TODO(), Request{Comment: c, parent: cp})
|
||||
require.NotNil(t, err)
|
||||
assert.Contains(t, err.Error(), "unexpected telegram status code 404", "send on broken tg")
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ func (s *private) createCommentCtrl(w http.ResponseWriter, r *http.Request) {
|
||||
Scopes(comment.Locator.URL, lastCommentsScope, comment.User.ID, comment.Locator.SiteID))
|
||||
|
||||
if s.notifyService != nil {
|
||||
s.notifyService.Submit(finalComment)
|
||||
s.notifyService.Submit(notify.Request{Comment: finalComment})
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] created commend %+v", finalComment)
|
||||
|
||||
Reference in New Issue
Block a user