From e1606159c82436449ad4cc1ffb0cc9126260c4fb Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 22 Feb 2022 13:24:24 +0100 Subject: [PATCH] add method to send message to groups --- changelog/unreleased/notifications-service.md | 5 ++ notifications/pkg/channels/channels.go | 78 +++++++++++++++---- notifications/pkg/service/service.go | 10 ++- 3 files changed, 77 insertions(+), 16 deletions(-) create mode 100644 changelog/unreleased/notifications-service.md diff --git a/changelog/unreleased/notifications-service.md b/changelog/unreleased/notifications-service.md new file mode 100644 index 00000000000..a6bc56932fa --- /dev/null +++ b/changelog/unreleased/notifications-service.md @@ -0,0 +1,5 @@ +Enhancement: Implement notifications service + +Implemented the minimal version of the notifications service to be able to notify a user when they received a share. + +https://github.com/owncloud/ocis/pull/3217 diff --git a/notifications/pkg/channels/channels.go b/notifications/pkg/channels/channels.go index c78068ca2f6..f4e427b018c 100644 --- a/notifications/pkg/channels/channels.go +++ b/notifications/pkg/channels/channels.go @@ -6,18 +6,20 @@ import ( "net/smtp" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + groups "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/owncloud/ocis/notifications/pkg/config" "github.com/owncloud/ocis/ocis-pkg/log" + "github.com/pkg/errors" ) // Channel defines the methods of a communication channel. type Channel interface { - // Todo(c0rby): Do we need a PrepareMessage method? - // Maybe channels need to format the message or will the caller - // of SendMessage do that? - // SendMessage sends a message in a channel specific way. - SendMessage(receiver, msg string) error + // SendMessage sends a message to users. + SendMessage(userIDs []string, msg string) error + // SendMessageToGroup sends a message to a group. + SendMessageToGroup(groupdID *groups.GroupId, msg string) error } // NewMailChannel instantiates a new mail communication channel. @@ -30,6 +32,7 @@ func NewMailChannel(cfg config.Config, logger log.Logger) (Channel, error) { return Mail{ gatewayClient: gc, conf: cfg, + logger: logger, }, nil } @@ -37,21 +40,66 @@ func NewMailChannel(cfg config.Config, logger log.Logger) (Channel, error) { type Mail struct { gatewayClient gateway.GatewayAPIClient conf config.Config + logger log.Logger } -func (m Mail) SendMessage(receiver, msg string) error { - smtpConf := m.conf.Notifications.SMTP - res, err := m.gatewayClient.Authenticate(context.Background(), &gateway.AuthenticateRequest{ - Type: "machine", - ClientId: "userid:" + receiver, - ClientSecret: m.conf.Notifications.MachineAuthSecret, - }) +// SendMessage sends a message to all given users. +func (m Mail) SendMessage(userIDs []string, msg string) error { + to, err := m.getReceiverAddresses(userIDs) if err != nil { return err } - - to := []string{res.User.Mail} body := []byte(msg) + + smtpConf := m.conf.Notifications.SMTP auth := smtp.PlainAuth("", smtpConf.Sender, smtpConf.Password, smtpConf.Host) - return smtp.SendMail(smtpConf.Host+":"+smtpConf.Port, auth, smtpConf.Sender, to, body) + if err := smtp.SendMail(smtpConf.Host+":"+smtpConf.Port, auth, smtpConf.Sender, to, body); err != nil { + return errors.Wrap(err, "could not send mail") + } + return nil +} + +// SendMessageToGroup sends a message to all members of the given group. +func (m Mail) SendMessageToGroup(groupID *groups.GroupId, msg string) error { + // TODO We need an authenticated context here... + res, err := m.gatewayClient.GetGroup(context.Background(), &groups.GetGroupRequest{GroupId: groupID}) + if err != nil { + return err + } + if res.Status.Code != rpc.Code_CODE_OK { + return errors.New("could not get group") + } + + members := make([]string, 0, len(res.Group.Members)) + for _, id := range res.Group.Members { + members = append(members, id.OpaqueId) + } + + return m.SendMessage(members, msg) +} + +func (m Mail) getReceiverAddresses(receivers []string) ([]string, error) { + addresses := make([]string, 0, len(receivers)) + for _, id := range receivers { + // Authenticate is too costly but at the moment our only option to get the user. + // We don't have an authenticated context so calling `GetUser` doesn't work. + res, err := m.gatewayClient.Authenticate(context.Background(), &gateway.AuthenticateRequest{ + Type: "machine", + ClientId: "userid:" + id, + ClientSecret: m.conf.Notifications.MachineAuthSecret, + }) + if err != nil { + return nil, err + } + if res.Status.Code != rpc.Code_CODE_OK { + m.logger.Error(). + Interface("status", res.Status). + Str("receiver_id", id). + Msg("could not get user") + continue + } + addresses = append(addresses, res.User.Mail) + } + + return addresses, nil } diff --git a/notifications/pkg/service/service.go b/notifications/pkg/service/service.go index e220bb9448f..81e4a26b4ac 100644 --- a/notifications/pkg/service/service.go +++ b/notifications/pkg/service/service.go @@ -40,9 +40,17 @@ func (s eventsNotifier) Run() error { go func() { switch e := evt.(type) { case events.ShareCreated: - if err := s.channel.SendMessage(e.GranteeUserID.OpaqueId, "You got a share"); err != nil { + msg := "You got a share!" + var err error + if e.GranteeUserID != nil { + err = s.channel.SendMessage([]string{e.GranteeUserID.OpaqueId}, msg) + } else if e.GranteeGroupID != nil { + err = s.channel.SendMessageToGroup(e.GranteeGroupID, msg) + } + if err != nil { s.logger.Error(). Err(err). + Str("event", "ShareCreated"). Msg("failed to send a message") } }