add UserDetailTelegram support

This commit is contained in:
Dmitry Verkhoturov
2021-06-13 13:18:33 -05:00
committed by Umputun
parent d6167980f4
commit fd4c6ceb18
11 changed files with 170 additions and 42 deletions
+26 -13
View File
@@ -35,13 +35,18 @@ type Destination interface {
type Store interface {
Get(locator store.Locator, id string, user store.User) (store.Comment, error)
GetUserEmail(siteID string, userID string) (string, error)
GetUserTelegram(siteID string, userID string) (string, error)
}
// used for email and telegram retrieval from user details
type getUserDetail func(string, string) (string, error)
// Request notification for a Comment
type Request struct {
Comment store.Comment
parent store.Comment
Emails []string
Comment store.Comment
parent store.Comment
Emails []string
Telegrams []string
}
// VerificationRequest notification for user
@@ -84,7 +89,8 @@ func (s *Service) Submit(req Request) {
if s.dataService != nil && req.Comment.ParentID != "" {
if p, err := s.dataService.Get(req.Comment.Locator, req.Comment.ParentID, store.User{}); err == nil {
req.parent = p
req.Emails = deduplicateStrings(s.getNotificationEmails(req, p))
req.Emails = s.getNotificationTargets(req, p, s.dataService.GetUserEmail)
req.Telegrams = s.getNotificationTargets(req, p, s.dataService.GetUserTelegram)
}
}
select {
@@ -94,25 +100,32 @@ func (s *Service) Submit(req Request) {
}
}
// getNotificationEmails returns list of emails for notifications for provided comment.
// Emails is not added to the returned list in case original message is from the same user as the notification receiver.
func (s *Service) getNotificationEmails(req Request, notifyComment store.Comment) (result []string) {
// getNotificationTargets returns list of notification targets (like email or telegram username) for users
// interested in notifications for provided comment.
// Targets are not added to the returned list in case the original message
// is from the same user as the notification receiver.
// Results are deduplicated.
func (s *Service) getNotificationTargets(
req Request,
notifyComment store.Comment,
getUserDetail getUserDetail,
) (result []string) {
// add current user email only if the user is not the one who wrote the original comment
if notifyComment.User.ID != req.Comment.User.ID {
email, err := s.dataService.GetUserEmail(req.Comment.Locator.SiteID, notifyComment.User.ID)
detail, err := getUserDetail(req.Comment.Locator.SiteID, notifyComment.User.ID)
if err != nil {
log.Printf("[WARN] can't read email for %s, %v", notifyComment.User.ID, err)
log.Printf("[WARN] can't read notification detail for %s, %v", notifyComment.User.ID, err)
}
if email != "" {
result = append(result, email)
if detail != "" {
result = append(result, detail)
}
}
if notifyComment.ParentID != "" {
if p, err := s.dataService.Get(req.Comment.Locator, notifyComment.ParentID, store.User{}); err == nil {
result = append(result, s.getNotificationEmails(req, p)...)
result = append(result, s.getNotificationTargets(req, p, getUserDetail)...)
}
}
return result
return deduplicateStrings(result)
}
// SubmitVerification to internal channel if not busy, drop if can't send
+20 -12
View File
@@ -139,13 +139,13 @@ func TestService_WithParent(t *testing.T) {
func TestService_EmailRetrieval(t *testing.T) {
dest := &MockDest{id: 1}
dataStore := &mockStore{data: map[string]store.Comment{}, emailData: map[string]string{}}
dataStore := &mockStore{data: map[string]store.Comment{}, userDetails: map[string]string{}}
dataStore.data["p1"] = store.Comment{ID: "p1", User: store.User{ID: "u1"}}
dataStore.data["p2"] = store.Comment{ID: "p2", ParentID: "p1", User: store.User{ID: "u1"}}
dataStore.data["p3"] = store.Comment{ID: "p3", ParentID: "p1", User: store.User{ID: "u2"}}
dataStore.data["p4"] = store.Comment{ID: "p4", ParentID: "p3", User: store.User{ID: "u1"}}
dataStore.emailData["u1"] = "u1@example.com"
dataStore.userDetails["u1"] = "u1@example.com"
s := NewService(dataStore, 1, dest)
assert.NotNil(t, s)
@@ -198,16 +198,16 @@ func TestService_EmailRetrieval(t *testing.T) {
func TestService_Recursive(t *testing.T) {
dest := &MockDest{id: 1}
dataStore := &mockStore{data: map[string]store.Comment{}, emailData: map[string]string{}}
dataStore := &mockStore{data: map[string]store.Comment{}, userDetails: map[string]string{}}
dataStore.data["p1"] = store.Comment{ID: "p1", User: store.User{ID: "u1"}}
dataStore.data["p2"] = store.Comment{ID: "p2", ParentID: "p1", User: store.User{ID: "u2"}}
dataStore.data["p3"] = store.Comment{ID: "p3", ParentID: "p2", User: store.User{ID: "u3"}}
dataStore.data["p4"] = store.Comment{ID: "p4", ParentID: "p3", User: store.User{ID: "u1"}}
dataStore.data["p5"] = store.Comment{ID: "p5", ParentID: "p4", User: store.User{ID: "u4"}}
dataStore.emailData["u1"] = "u1@example.com"
dataStore.userDetails["u1"] = "u1@example.com"
// second comment goes without email address for notification
dataStore.emailData["u3"] = "u3@example.com"
dataStore.userDetails["u3"] = "u3@example.com"
s := NewService(dataStore, 1, dest)
assert.NotNil(t, s)
@@ -277,8 +277,16 @@ func TestService_Nop(t *testing.T) {
}
type mockStore struct {
data map[string]store.Comment
emailData map[string]string
data map[string]store.Comment
userDetails map[string]string
}
func (m mockStore) getUserDetail(userID string) (string, error) {
detail, ok := m.userDetails[userID]
if !ok {
return "", errors.New("no such user")
}
return detail, nil
}
func (m mockStore) Get(_ store.Locator, id string, _ store.User) (store.Comment, error) {
@@ -290,9 +298,9 @@ func (m mockStore) Get(_ store.Locator, id string, _ store.User) (store.Comment,
}
func (m mockStore) GetUserEmail(_, userID string) (string, error) {
email, ok := m.emailData[userID]
if !ok {
return "", errors.New("no such user")
}
return email, nil
return m.getUserDetail(userID)
}
func (m mockStore) GetUserTelegram(_, userID string) (string, error) {
return m.getUserDetail(userID)
}