mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-12 14:30:19 -05:00
Merge pull request #10792 from bastianbeier/issue-10790
Add filtering of mails based on settings
This commit is contained in:
@@ -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 ¬ificationFilter{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
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user