diff --git a/services/notifications/pkg/email/templates/shares/shareExpired.email.subject.tmpl b/services/notifications/pkg/email/templates/shares/shareExpired.email.subject.tmpl index 3491d961a..e9593dcd5 100644 --- a/services/notifications/pkg/email/templates/shares/shareExpired.email.subject.tmpl +++ b/services/notifications/pkg/email/templates/shares/shareExpired.email.subject.tmpl @@ -1 +1 @@ -Share to '{{ .ShareFolder }}' expired at {{ .ExpiredAt }} +Share to '{{ .ShareFolder }}' expired at {{ .ExpiredAt }} \ No newline at end of file diff --git a/services/notifications/pkg/email/templates/spaces/membershipExpired.email.subject.tmpl b/services/notifications/pkg/email/templates/spaces/membershipExpired.email.subject.tmpl index d8c96f39a..936581838 100644 --- a/services/notifications/pkg/email/templates/spaces/membershipExpired.email.subject.tmpl +++ b/services/notifications/pkg/email/templates/spaces/membershipExpired.email.subject.tmpl @@ -1 +1 @@ -Membership of '{{ .SpaceName }}' expired at {{ .ExpiredAt }} +Membership of '{{ .SpaceName }}' expired at {{ .ExpiredAt }} \ No newline at end of file diff --git a/services/notifications/pkg/email/templates/spaces/unsharedSpace.email.subject.tmpl b/services/notifications/pkg/email/templates/spaces/unsharedSpace.email.subject.tmpl index b4a526abd..c6628dfb3 100644 --- a/services/notifications/pkg/email/templates/spaces/unsharedSpace.email.subject.tmpl +++ b/services/notifications/pkg/email/templates/spaces/unsharedSpace.email.subject.tmpl @@ -1 +1 @@ -{{ .SpaceSharer }} removed you from {{ .SpaceName }} +{{ .SpaceSharer }} removed you from {{ .SpaceName }} \ No newline at end of file diff --git a/services/notifications/pkg/service/notification_suite_test.go b/services/notifications/pkg/service/notification_suite_test.go new file mode 100644 index 000000000..5735c73ff --- /dev/null +++ b/services/notifications/pkg/service/notification_suite_test.go @@ -0,0 +1,13 @@ +package service_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestNotifications(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Notification Suite") +} diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go index eb0ecd4d1..676f3dfa0 100644 --- a/services/notifications/pkg/service/service.go +++ b/services/notifications/pkg/service/service.go @@ -15,7 +15,6 @@ import ( user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" "github.com/cs3org/reva/v2/pkg/events" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/notifications/pkg/channels" @@ -144,7 +143,7 @@ func (s eventsNotifier) getGranteeName(ctx context.Context, u *user.UserId, g *g } -func (s eventsNotifier) getResourceInfo(ctx context.Context, resourceID *providerv1beta1.ResourceId, fieldmask *fieldmaskpb.FieldMask) (*provider.ResourceInfo, error) { +func (s eventsNotifier) getResourceInfo(ctx context.Context, resourceID *provider.ResourceId, fieldmask *fieldmaskpb.FieldMask) (*provider.ResourceInfo, error) { // TODO: maybe cache this stat to reduce storage iops md, err := s.gwClient.Stat(ctx, &provider.StatRequest{ Ref: &provider.Reference{ diff --git a/services/notifications/pkg/service/service_test.go b/services/notifications/pkg/service/service_test.go new file mode 100644 index 000000000..340b2f149 --- /dev/null +++ b/services/notifications/pkg/service/service_test.go @@ -0,0 +1,153 @@ +package service_test + +import ( + "context" + "time" + + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + group "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" + user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/v2/pkg/events" + "github.com/cs3org/reva/v2/pkg/utils" + cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/owncloud/ocis/v2/ocis-pkg/log" + "github.com/owncloud/ocis/v2/services/notifications/pkg/service" + "github.com/test-go/testify/mock" +) + +var _ = Describe("Notifications", func() { + var ( + gwc *cs3mocks.GatewayAPIClient + sharer = &user.User{ + Id: &user.UserId{ + OpaqueId: "sharer", + }, + DisplayName: "Dr. S. Harer", + } + sharee = &user.User{ + Id: &user.UserId{ + OpaqueId: "sharee", + }, + DisplayName: "Eric Expireling", + } + resourceid = &provider.ResourceId{ + StorageId: "storageid", + SpaceId: "spaceid", + OpaqueId: "itemid", + } + ) + + BeforeEach(func() { + gwc = &cs3mocks.GatewayAPIClient{} + gwc.On("GetUser", mock.Anything, mock.Anything).Return(&user.GetUserResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil) + gwc.On("Authenticate", mock.Anything, mock.Anything).Return(&gateway.AuthenticateResponse{Status: &rpc.Status{Code: rpc.Code_CODE_OK}, User: sharer}, nil) + gwc.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) + }) + + DescribeTable("Sending notifications", + func(tc testChannel, ev interface{}) { + ch := make(chan interface{}) + evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gwc, "", "", "") + go evts.Run() + + ch <- ev + select { + case <-tc.done: + // finished + case <-time.Tick(3 * time.Second): + Fail("timeout waiting for notification") + } + }, + + Entry("Share Created", testChannel{ + expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true}, + expectedSubject: "Dr. S. Harer shared 'secrets of the board' with you", + expectedSender: sharer.GetDisplayName(), + done: make(chan struct{}), + }, events.ShareCreated{ + Sharer: sharer.GetId(), + GranteeUserID: sharee.GetId(), + CTime: utils.TimeToTS(time.Date(2023, 4, 17, 16, 42, 0, 0, time.UTC)), + ItemID: resourceid, + }), + + Entry("Share Expired", testChannel{ + expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true}, + expectedSubject: "Share to 'secrets of the board' expired at 2023-04-17 16:42:00", + expectedSender: sharer.GetDisplayName(), + done: make(chan struct{}), + }, events.ShareExpired{ + ShareOwner: sharer.GetId(), + GranteeUserID: sharee.GetId(), + ExpiredAt: time.Date(2023, 4, 17, 16, 42, 0, 0, time.UTC), + ItemID: resourceid, + }), + + Entry("Added to Space", testChannel{ + expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true}, + expectedSubject: "Dr. S. Harer invited you to join secret space", + expectedSender: sharer.GetDisplayName(), + done: make(chan struct{}), + }, events.SpaceShared{ + Executant: sharer.GetId(), + Creator: sharer.GetId(), + GranteeUserID: sharee.GetId(), + ID: &provider.StorageSpaceId{OpaqueId: "spaceid"}, + }), + + Entry("Removed from Space", testChannel{ + expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true}, + expectedSubject: "Dr. S. Harer removed you from secret space", + expectedSender: sharer.GetDisplayName(), + done: make(chan struct{}), + }, events.SpaceUnshared{ + Executant: sharer.GetId(), + GranteeUserID: sharee.GetId(), + ID: &provider.StorageSpaceId{OpaqueId: "spaceid"}, + }), + + Entry("Space Expired", testChannel{ + expectedReceipients: map[string]bool{sharee.GetId().GetOpaqueId(): true}, + expectedSubject: "Membership of 'secret space' expired at 2023-04-17 16:42:00", + expectedSender: sharer.GetDisplayName(), + done: make(chan struct{}), + }, events.SpaceMembershipExpired{ + SpaceOwner: sharer.GetId(), + GranteeUserID: sharee.GetId(), + SpaceID: &provider.StorageSpaceId{OpaqueId: "spaceid"}, + SpaceName: "secret space", + ExpiredAt: time.Date(2023, 4, 17, 16, 42, 0, 0, time.UTC), + }), + ) +}) + +// NOTE: This is explictitly not testing the message itself. Should we? +type testChannel struct { + expectedReceipients map[string]bool + expectedSubject string + expectedSender string + done chan struct{} +} + +func (tc testChannel) SendMessage(ctx context.Context, userIDs []string, msg, subject, senderDisplayName string) error { + defer GinkgoRecover() + + for _, u := range userIDs { + Expect(tc.expectedReceipients[u]).To(Equal(true)) + } + + // TODO: test the message? + //Expect(msg).To(Equal(tc.expectedMessage)) + Expect(subject).To(Equal(tc.expectedSubject)) + Expect(senderDisplayName).To(Equal(tc.expectedSender)) + tc.done <- struct{}{} + return nil +} + +func (tc testChannel) SendMessageToGroup(ctx context.Context, groupID *group.GroupId, msg, subject, senderDisplayName string) error { + return tc.SendMessage(ctx, []string{groupID.GetOpaqueId()}, msg, subject, senderDisplayName) +} diff --git a/services/notifications/pkg/service/shares.go b/services/notifications/pkg/service/shares.go index 0709f3f33..675884943 100644 --- a/services/notifications/pkg/service/shares.go +++ b/services/notifications/pkg/service/shares.go @@ -86,7 +86,7 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) { msg, subj, err := s.render("shares/shareExpired.email.body.tmpl", "shares/shareExpired.email.subject.tmpl", map[string]string{ "ShareGrantee": shareGrantee, - "ShareFolder": resourceInfo.Name, + "ShareFolder": resourceInfo.GetName(), "ExpiredAt": e.ExpiredAt.Format("2006-01-02 15:04:05"), }) diff --git a/services/notifications/pkg/service/spaces.go b/services/notifications/pkg/service/spaces.go index 3c4684967..44affdfcc 100644 --- a/services/notifications/pkg/service/spaces.go +++ b/services/notifications/pkg/service/spaces.go @@ -57,7 +57,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) { msg, subj, err := s.render("spaces/sharedSpace.email.body.tmpl", "spaces/sharedSpace.email.subject.tmpl", map[string]string{ "SpaceGrantee": spaceGrantee, "SpaceSharer": sharerDisplayName, - "SpaceName": resourceInfo.GetSpace().Name, + "SpaceName": resourceInfo.GetSpace().GetName(), "ShareLink": shareLink, }) if err != nil { @@ -136,7 +136,7 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) { func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExpired) { logger := s.logger.With(). Str("event", "SpaceMembershipExpired"). - Str("itemid", e.SpaceID). + Str("itemid", e.SpaceID.GetOpaqueId()). Logger() ctx, owner, err := utils.Impersonate(e.SpaceOwner, s.gwClient, s.machineAuthAPIKey)