Merge pull request #10792 from bastianbeier/issue-10790

Add filtering of mails based on settings
This commit is contained in:
kobergj
2024-12-23 11:00:27 +01:00
committed by GitHub
7 changed files with 200 additions and 11 deletions
@@ -0,0 +1,6 @@
Enhancement: Part III: Filtering of mail notifications
Part III: Mail notifications are now filtered based on the notification preferences in the user settings
https://github.com/owncloud/ocis/pull/10792
https://github.com/owncloud/ocis/issues/10790
@@ -0,0 +1,61 @@
package service
import (
"context"
"errors"
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/ocis-pkg/middleware"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
micrometadata "go-micro.dev/v4/metadata"
)
type notificationFilter struct {
log log.Logger
valueClient settingssvc.ValueService
}
func newNotificationFilter(l log.Logger, vc settingssvc.ValueService) *notificationFilter {
return &notificationFilter{log: l, valueClient: vc}
}
// execute removes users who have disabled mail notifications for the event
func (nf notificationFilter) execute(ctx context.Context, users []*user.User, settingId string) []*user.User {
var filteredUsers []*user.User
for _, u := range users {
userId := u.GetId().GetOpaqueId()
enabled, err := getSetting(ctx, nf.valueClient, userId, settingId)
if err != nil {
nf.log.Error().Err(err).Str("userId", userId).Str("settingId", settingId).Msg("cannot get user event setting")
continue
}
if enabled {
filteredUsers = append(filteredUsers, u)
}
}
return filteredUsers
}
func getSetting(ctx context.Context, vc settingssvc.ValueService, userId string, settingId string) (bool, error) {
resp, err := vc.GetValueByUniqueIdentifiers(
micrometadata.Set(ctx, middleware.AccountID, userId),
&settingssvc.GetValueByUniqueIdentifiersRequest{
AccountUuid: userId,
SettingId: settingId,
},
)
if err != nil {
return false, err
}
val := resp.GetValue().GetValue().GetCollectionValue().GetValues()
for _, option := range val {
if option.GetKey() == "mail" {
return option.GetBoolValue(), nil
}
}
return false, errors.New("no setting found")
}
@@ -0,0 +1,79 @@
package service
import (
"context"
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
"github.com/owncloud/ocis/v2/ocis-pkg/log"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
settings "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"go-micro.dev/v4/client"
"testing"
)
var testLogger = log.NewLogger()
func TestNotificationFilter_execute(t *testing.T) {
type args struct {
ctx context.Context
users []*user.User
settingId string
}
tests := []struct {
name string
vc settings.ValueService
args args
want []*user.User
}{
{"no connection to ValueService", settings.MockValueService{
GetValueByUniqueIdentifiersFunc: func(ctx context.Context, req *settings.GetValueByUniqueIdentifiersRequest, opts ...client.CallOption) (*settings.GetValueResponse, error) {
return nil, errors.New("no connection to ValueService")
},
}, args{users: []*user.User{{Id: &user.UserId{OpaqueId: "foo"}}}, settingId: "bar", ctx: context.TODO()}, []*user.User(nil)},
{"no setting in ValueService response", settings.MockValueService{
GetValueByUniqueIdentifiersFunc: func(ctx context.Context, req *settings.GetValueByUniqueIdentifiersRequest, opts ...client.CallOption) (*settings.GetValueResponse, error) {
return &settings.GetValueResponse{}, nil
},
}, args{users: []*user.User{{Id: &user.UserId{OpaqueId: "foo"}}}, settingId: "bar", ctx: context.TODO()}, []*user.User(nil)},
{"ValueService nil response", settings.MockValueService{
GetValueByUniqueIdentifiersFunc: func(ctx context.Context, req *settings.GetValueByUniqueIdentifiersRequest, opts ...client.CallOption) (*settings.GetValueResponse, error) {
return nil, nil
},
}, args{users: []*user.User{{Id: &user.UserId{OpaqueId: "foo"}}}, settingId: "bar", ctx: context.TODO()}, []*user.User(nil)},
{"Event enabled", setupMockValueService(true), args{users: []*user.User{{Id: &user.UserId{OpaqueId: "foo"}}}, settingId: "bar", ctx: context.TODO()}, []*user.User{{Id: &user.UserId{OpaqueId: "foo"}}}},
{"Event disabled", setupMockValueService(false), args{users: []*user.User{{Id: &user.UserId{OpaqueId: "foo"}}}, settingId: "bar", ctx: context.TODO()}, []*user.User(nil)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ulf := notificationFilter{
log: testLogger,
valueClient: tt.vc,
}
assert.Equal(t, tt.want, ulf.execute(tt.args.ctx, tt.args.users, tt.args.settingId))
})
}
}
func setupMockValueService(mail bool) settings.ValueService {
return settings.MockValueService{
GetValueByUniqueIdentifiersFunc: func(ctx context.Context, req *settings.GetValueByUniqueIdentifiersRequest, opts ...client.CallOption) (*settings.GetValueResponse, error) {
return &settings.GetValueResponse{
Value: &settingsmsg.ValueWithIdentifier{
Value: &settingsmsg.Value{
Value: &settingsmsg.Value_CollectionValue{
CollectionValue: &settingsmsg.CollectionValue{
Values: []*settingsmsg.CollectionOption{
{
Key: "mail",
Option: &settingsmsg.CollectionOption_BoolValue{BoolValue: mail},
},
},
},
},
},
},
}, nil
},
}
}
@@ -65,6 +65,7 @@ func NewEventsNotifier(
defaultLanguage: defaultLanguage,
ocisURL: ocisURL,
translationPath: translationPath,
filter: newNotificationFilter(logger, valueService),
}
}
@@ -81,6 +82,7 @@ type eventsNotifier struct {
ocisURL string
serviceAccountID string
serviceAccountSecret string
filter *notificationFilter
}
func (s eventsNotifier) Run() error {
@@ -2,6 +2,7 @@ package service_test
import (
"context"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
"time"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
@@ -68,7 +69,22 @@ var _ = Describe("Notifications", func() {
gatewayClient.On("Stat", mock.Anything, mock.Anything).Return(&provider.StatResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, Info: &provider.ResourceInfo{Name: "secrets of the board", Space: &provider.StorageSpace{Name: "secret space"}}}, nil)
vs = &settingssvc.MockValueService{}
vs.GetValueByUniqueIdentifiersFunc = func(ctx context.Context, req *settingssvc.GetValueByUniqueIdentifiersRequest, opts ...client.CallOption) (*settingssvc.GetValueResponse, error) {
return nil, nil
return &settingssvc.GetValueResponse{
Value: &settingsmsg.ValueWithIdentifier{
Value: &settingsmsg.Value{
Value: &settingsmsg.Value_CollectionValue{
CollectionValue: &settingsmsg.CollectionValue{
Values: []*settingsmsg.CollectionOption{
{
Key: "mail",
Option: &settingsmsg.CollectionOption_BoolValue{BoolValue: true},
},
},
},
},
},
},
}, nil
}
})
@@ -266,7 +282,22 @@ var _ = Describe("Notifications X-Site Scripting", func() {
}, nil)
vs = &settingssvc.MockValueService{}
vs.GetValueByUniqueIdentifiersFunc = func(ctx context.Context, req *settingssvc.GetValueByUniqueIdentifiersRequest, opts ...client.CallOption) (*settingssvc.GetValueResponse, error) {
return nil, nil
return &settingssvc.GetValueResponse{
Value: &settingsmsg.ValueWithIdentifier{
Value: &settingsmsg.Value{
Value: &settingsmsg.Value_CollectionValue{
CollectionValue: &settingsmsg.CollectionValue{
Values: []*settingsmsg.CollectionOption{
{
Key: "mail",
Option: &settingsmsg.CollectionOption_BoolValue{BoolValue: true},
},
},
},
},
},
},
}, nil
}
})
+7 -4
View File
@@ -4,6 +4,7 @@ import (
"github.com/cs3org/reva/v2/pkg/events"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/owncloud/ocis/v2/services/notifications/pkg/email"
"github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)
@@ -48,7 +49,8 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
}
granteeList := s.ensureGranteeList(ctx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
if granteeList == nil {
filteredGrantees := s.filter.execute(ctx, granteeList, defaults.SettingUUIDProfileEventShareCreated)
if filteredGrantees == nil {
return
}
@@ -59,7 +61,7 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
"ShareSharer": sharerDisplayName,
"ShareFolder": resourceInfo.Name,
"ShareLink": shareLink,
}, granteeList, sharerDisplayName)
}, filteredGrantees, sharerDisplayName)
if err != nil {
s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("could not get render the email")
return
@@ -100,7 +102,8 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
}
granteeList := s.ensureGranteeList(ctx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
if granteeList == nil {
filteredGrantees := s.filter.execute(ctx, granteeList, defaults.SettingUUIDProfileEventShareExpired)
if filteredGrantees == nil {
return
}
@@ -109,7 +112,7 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
map[string]string{
"ShareFolder": resourceInfo.GetName(),
"ExpiredAt": e.ExpiredAt.Format("2006-01-02 15:04:05"),
}, granteeList, owner.GetDisplayName())
}, filteredGrantees, owner.GetDisplayName())
if err != nil {
s.logger.Error().Err(err).Str("event", "ShareExpired").Msg("could not get render the email")
return
+12 -5
View File
@@ -5,6 +5,7 @@ import (
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/owncloud/ocis/v2/services/notifications/pkg/email"
"github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults"
)
func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
@@ -63,7 +64,8 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
// the Grantees of the shares. Ideally the notfication service would use some kind of service
// user for this.
granteeList := s.ensureGranteeList(ctx, executant.GetId(), e.GranteeUserID, e.GranteeGroupID)
if granteeList == nil {
filteredGrantees := s.filter.execute(ctx, granteeList, defaults.SettingUUIDProfileEventSpaceShared)
if filteredGrantees == nil {
return
}
@@ -74,7 +76,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
"SpaceSharer": sharerDisplayName,
"SpaceName": resourceInfo.GetSpace().GetName(),
"ShareLink": shareLink,
}, granteeList, sharerDisplayName)
}, filteredGrantees, sharerDisplayName)
if err != nil {
s.logger.Error().Err(err).Str("event", "SharedSpace").Msg("could not get render the email")
return
@@ -136,7 +138,8 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
// the Grantees of the shares. Ideally the notfication service would use some kind of service
// user for this.
granteeList := s.ensureGranteeList(ctx, executant.GetId(), e.GranteeUserID, e.GranteeGroupID)
if granteeList == nil {
filteredGrantees := s.filter.execute(ctx, granteeList, defaults.SettingUUIDProfileEventSpaceUnshared)
if filteredGrantees == nil {
return
}
@@ -147,7 +150,7 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
"SpaceSharer": sharerDisplayName,
"SpaceName": resourceInfo.GetSpace().Name,
"ShareLink": shareLink,
}, granteeList, sharerDisplayName)
}, filteredGrantees, sharerDisplayName)
if err != nil {
s.logger.Error().Err(err).Str("event", "UnsharedSpace").Msg("Could not get render the email")
return
@@ -185,13 +188,17 @@ func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExp
if granteeList == nil {
return
}
filteredGrantees := s.filter.execute(ctx, granteeList, defaults.SettingUUIDProfileEventSpaceMembershipExpired)
if filteredGrantees == nil {
return
}
recipientList, err := s.render(ctx, email.MembershipExpired,
"SpaceGrantee",
map[string]string{
"SpaceName": e.SpaceName,
"ExpiredAt": e.ExpiredAt.Format("2006-01-02 15:04:05"),
}, granteeList, owner.GetDisplayName())
}, filteredGrantees, owner.GetDisplayName())
if err != nil {
s.logger.Error().Err(err).Str("event", "SpaceUnshared").Msg("could not get render the email")
return