Skip to content

Commit 461d7f2

Browse files
committed
Persist log of events for historical tracking and preventing duplicate sends.
1 parent d70178c commit 461d7f2

File tree

13 files changed

+303
-18
lines changed

13 files changed

+303
-18
lines changed

data/raw/service/test/client_mocks.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

data/raw/test/client_mocks.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

data/service/api/standard.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
dataStore "github.com/tidepool-org/platform/data/store"
1616
"github.com/tidepool-org/platform/errors"
1717
"github.com/tidepool-org/platform/metric"
18+
"github.com/tidepool-org/platform/notifications/logs"
1819
"github.com/tidepool-org/platform/permission"
1920
"github.com/tidepool-org/platform/service"
2021
serviceApi "github.com/tidepool-org/platform/service/api"
@@ -33,14 +34,15 @@ type Standard struct {
3334
dataRawClient dataRaw.Client
3435
dataSourceClient dataSourceService.Client
3536
workClient work.Client
37+
notifLogger logs.Logger
3638
abbottServiceRequestAuthorizer abbottService.RequestAuthorizer
3739
twiistServiceAccountAuthorizer auth.ServiceAccountAuthorizer
3840
}
3941

4042
func NewStandard(svc service.Service, metricClient metric.Client, permissionClient permission.Client,
4143
dataDeduplicatorFactory dataDuplicator.Factory,
4244
store dataStore.Store, syncTaskStore syncTaskStore.Store, dataClient dataClient.Client,
43-
dataRawClient dataRaw.Client, dataSourceClient dataSourceService.Client, workClient work.Client,
45+
dataRawClient dataRaw.Client, dataSourceClient dataSourceService.Client, workClient work.Client, notifLogger logs.Logger,
4446
abbottServiceRequestAuthorizer abbottService.RequestAuthorizer,
4547
twiistServiceAccountAuthorizer auth.ServiceAccountAuthorizer) (*Standard, error) {
4648
if metricClient == nil {
@@ -70,6 +72,9 @@ func NewStandard(svc service.Service, metricClient metric.Client, permissionClie
7072
if workClient == nil {
7173
return nil, errors.New("work client is missing")
7274
}
75+
if notifLogger == nil {
76+
return nil, errors.New("notifications logger is missing")
77+
}
7378
if abbottServiceRequestAuthorizer == nil {
7479
return nil, errors.New("abbott service request authorizer is missing")
7580
}
@@ -92,6 +97,7 @@ func NewStandard(svc service.Service, metricClient metric.Client, permissionClie
9297
dataClient: dataClient,
9398
dataRawClient: dataRawClient,
9499
dataSourceClient: dataSourceClient,
100+
notifLogger: notifLogger,
95101
workClient: workClient,
96102
abbottServiceRequestAuthorizer: abbottServiceRequestAuthorizer,
97103
twiistServiceAccountAuthorizer: twiistServiceAccountAuthorizer,
@@ -125,6 +131,6 @@ func (s *Standard) withContext(handler dataService.HandlerFunc) rest.HandlerFunc
125131
return dataServiceContext.WithContext(s.AuthClient(), s.metricClient, s.permissionClient,
126132
s.dataDeduplicatorFactory,
127133
s.dataStore, s.syncTaskStore, s.dataClient,
128-
s.dataRawClient, s.dataSourceClient, s.workClient,
134+
s.dataRawClient, s.dataSourceClient, s.workClient, s.notifLogger,
129135
s.abbottServiceRequestAuthorizer, s.twiistServiceAccountAuthorizer, handler)
130136
}

data/service/api/v1/notifications.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func QueueClaimAccountNotification(dataServiceContext dataService.Context) {
3232
return
3333
}
3434

35-
if err := claims.AddWorkItem(req.Context(), dataServiceContext.WorkClient(), data); err != nil {
35+
if err := claims.AddWorkItem(req.Context(), dataServiceContext.WorkClient(), dataServiceContext.NotifLogger(), data); err != nil {
3636
responder.Error(http.StatusInternalServerError, err)
3737
return
3838
}
@@ -52,7 +52,7 @@ func QueueConnectAccountNotification(dataServiceContext dataService.Context) {
5252
return
5353
}
5454

55-
if err := connrequests.AddWorkItem(req.Context(), dataServiceContext.WorkClient(), data); err != nil {
55+
if err := connrequests.AddWorkItem(req.Context(), dataServiceContext.WorkClient(), dataServiceContext.NotifLogger(), data); err != nil {
5656
responder.Error(http.StatusInternalServerError, err)
5757
return
5858
}
@@ -72,7 +72,7 @@ func SendDeviceIssuesNotification(dataServiceContext dataService.Context) {
7272
return
7373
}
7474

75-
if err := connissues.AddWorkItem(req.Context(), dataServiceContext.WorkClient(), data); err != nil {
75+
if err := connissues.AddWorkItem(req.Context(), dataServiceContext.WorkClient(), dataServiceContext.NotifLogger(), data); err != nil {
7676
responder.Error(http.StatusInternalServerError, err)
7777
return
7878
}

data/service/context.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
dataSourceService "github.com/tidepool-org/platform/data/source/service"
1313
dataStore "github.com/tidepool-org/platform/data/store"
1414
"github.com/tidepool-org/platform/metric"
15+
"github.com/tidepool-org/platform/notifications/logs"
1516
"github.com/tidepool-org/platform/permission"
1617
"github.com/tidepool-org/platform/service"
1718
"github.com/tidepool-org/platform/summary"
@@ -42,6 +43,7 @@ type Context interface {
4243
DataRawClient() dataRaw.Client
4344
DataSourceClient() dataSourceService.Client
4445
WorkClient() work.Client
46+
NotifLogger() logs.Logger
4547

4648
AbbottServiceRequestAuthorizer() abbottService.RequestAuthorizer
4749
TwiistServiceAccountAuthorizer() auth.ServiceAccountAuthorizer

data/service/context/standard.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
dataStore "github.com/tidepool-org/platform/data/store"
2020
"github.com/tidepool-org/platform/errors"
2121
"github.com/tidepool-org/platform/metric"
22+
"github.com/tidepool-org/platform/notifications/logs"
2223
"github.com/tidepool-org/platform/permission"
2324
serviceContext "github.com/tidepool-org/platform/service/context"
2425
"github.com/tidepool-org/platform/summary"
@@ -46,6 +47,7 @@ type Standard struct {
4647
dataRawClient dataRaw.Client
4748
dataSourceClient dataSourceService.Client
4849
workClient work.Client
50+
notifLogger logs.Logger
4951
alertsRepository alerts.Repository
5052
abbottServiceRequestAuthorizer abbottService.RequestAuthorizer
5153
twiistServiceAccountAuthorizer auth.ServiceAccountAuthorizer
@@ -54,14 +56,14 @@ type Standard struct {
5456
func WithContext(authClient auth.Client, metricClient metric.Client, permissionClient permission.Client,
5557
dataDeduplicatorFactory dataDeduplicator.Factory,
5658
store dataStore.Store, syncTaskStore syncTaskStore.Store, dataClient dataClient.Client,
57-
dataRawClient dataRaw.Client, dataSourceClient dataSourceService.Client, workClient work.Client,
59+
dataRawClient dataRaw.Client, dataSourceClient dataSourceService.Client, workClient work.Client, notifLogger logs.Logger,
5860
abbottServiceRequestAuthorizer abbottService.RequestAuthorizer,
5961
twiistServiceAccountAuthorizer auth.ServiceAccountAuthorizer,
6062
handler dataService.HandlerFunc) rest.HandlerFunc {
6163
return func(response rest.ResponseWriter, request *rest.Request) {
6264
standard, standardErr := NewStandard(response, request, authClient, metricClient, permissionClient,
6365
dataDeduplicatorFactory, store, syncTaskStore, dataClient, dataRawClient, dataSourceClient,
64-
workClient, abbottServiceRequestAuthorizer, twiistServiceAccountAuthorizer)
66+
workClient, notifLogger, abbottServiceRequestAuthorizer, twiistServiceAccountAuthorizer)
6567
if standardErr != nil {
6668
if responder, responderErr := serviceContext.NewResponder(response, request); responderErr != nil {
6769
response.WriteHeader(http.StatusInternalServerError)
@@ -80,7 +82,7 @@ func NewStandard(response rest.ResponseWriter, request *rest.Request,
8082
authClient auth.Client, metricClient metric.Client, permissionClient permission.Client,
8183
dataDeduplicatorFactory dataDeduplicator.Factory,
8284
store dataStore.Store, syncTaskStore syncTaskStore.Store, dataClient dataClient.Client,
83-
dataRawClient dataRaw.Client, dataSourceClient dataSourceService.Client, workClient work.Client,
85+
dataRawClient dataRaw.Client, dataSourceClient dataSourceService.Client, workClient work.Client, notifLogger logs.Logger,
8486
abbottServiceRequestAuthorizer abbottService.RequestAuthorizer,
8587
twiistServiceAccountAuthorizer auth.ServiceAccountAuthorizer) (*Standard, error) {
8688
if authClient == nil {
@@ -113,6 +115,9 @@ func NewStandard(response rest.ResponseWriter, request *rest.Request,
113115
if workClient == nil {
114116
return nil, errors.New("work client is missing")
115117
}
118+
if notifLogger == nil {
119+
return nil, errors.New("notifications logger is missing")
120+
}
116121
if abbottServiceRequestAuthorizer == nil {
117122
return nil, errors.New("abbott service request authorizer is missing")
118123
}
@@ -137,6 +142,7 @@ func NewStandard(response rest.ResponseWriter, request *rest.Request,
137142
dataRawClient: dataRawClient,
138143
dataSourceClient: dataSourceClient,
139144
workClient: workClient,
145+
notifLogger: notifLogger,
140146
abbottServiceRequestAuthorizer: abbottServiceRequestAuthorizer,
141147
twiistServiceAccountAuthorizer: twiistServiceAccountAuthorizer,
142148
}, nil
@@ -269,3 +275,7 @@ func (s *Standard) AlertsRepository() alerts.Repository {
269275
func (s *Standard) GetMongoClient() *mongo.Client {
270276
return s.dataStore.GetClient()
271277
}
278+
279+
func (s *Standard) NotifLogger() logs.Logger {
280+
return s.notifLogger
281+
}

data/service/service/standard.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import (
5454
workStoreStructuredMongo "github.com/tidepool-org/platform/work/store/structured/mongo"
5555

5656
notifications "github.com/tidepool-org/platform/notifications"
57+
"github.com/tidepool-org/platform/notifications/logs"
5758
"github.com/tidepool-org/platform/notifications/work/claims"
5859
connissues "github.com/tidepool-org/platform/notifications/work/connections/issues"
5960
connreqs "github.com/tidepool-org/platform/notifications/work/connections/requests"
@@ -76,6 +77,7 @@ type Standard struct {
7677
mailerClient mailer.Mailer
7778
summaryClient *summaryClient.Client
7879
workClient *workService.Client
80+
notifLogger *logs.LogsRepository
7981
abbottClient *abbottClient.Client
8082
userClient user.Client
8183
confirmationClient confirmationClient.ClientWithResponsesInterface
@@ -703,11 +705,13 @@ func (s *Standard) initializeWorkCoordinator() error {
703705
return errors.Wrap(err, "unable to register abbott processors")
704706
}
705707

708+
s.notifLogger = logs.NewLogsRepository(s.dataRawStructuredStore.Store)
706709
notificationsDependencies := notifications.Dependencies{
707710
Auth: s.AuthClient(),
708711
Clinics: s.clinicsClient,
709712
Confirmation: s.confirmationClient,
710713
DataSources: s.dataSourceStructuredStore.NewDataSourcesRepository(),
714+
Logs: s.notifLogger,
711715
Mailer: s.mailerClient,
712716
Users: s.userClient,
713717
Worker: s.workClient,
@@ -764,7 +768,7 @@ func (s *Standard) initializeAPI() error {
764768
newAPI, err := api.NewStandard(s, s.metricClient, s.permissionClient,
765769
s.dataDeduplicatorFactory,
766770
s.dataStore, s.syncTaskStore, s.dataClient,
767-
s.dataRawClient, s.dataSourceClient, s.workClient,
771+
s.dataRawClient, s.dataSourceClient, s.workClient, s.notifLogger,
768772
s.abbottClient, s.twiistServiceAccountAuthorizer)
769773
if err != nil {
770774
return errors.Wrap(err, "unable to create api")

notifications/logs/logs.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package logs
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"go.mongodb.org/mongo-driver/bson"
8+
9+
"github.com/tidepool-org/platform/structure"
10+
)
11+
12+
const (
13+
StatusWorkQueued string = "work-queued"
14+
StatusWorkError string = "work-error"
15+
StatusWorkConditionsExpired string = "work-conditions-expired"
16+
StatusEmailAttempted string = "email-attempted"
17+
StatusEmailError string = "email-error"
18+
StatusEmailCompleted string = "email-completed"
19+
)
20+
21+
type Logger interface {
22+
Add(ctx context.Context, entry LogEntry) error
23+
List(ctx context.Context, filter Filter) ([]LogEntry, error)
24+
}
25+
26+
type Filter struct {
27+
Type string
28+
GroupID string
29+
UserID string
30+
DataSourceID string
31+
}
32+
33+
type LogEntry struct {
34+
Type string `bson:"type,omitempty"`
35+
Email string `bson:"email,omitempty"`
36+
GroupID string `bson:"groupID,omitempty"`
37+
UserID string `bson:"userId,omitempty"`
38+
DataSourceID string `bson:"dataSourceId,omitempty"`
39+
CreatedAt time.Time `bson:"createdAt,omitzero,omitempty"`
40+
Status string `bson:"status,omitempty"`
41+
Metadata bson.M `bson:"metadata,omitempty"`
42+
}
43+
44+
func (l *LogEntry) Validate(validator structure.Validator) {
45+
validator.String("type", &l.Type).NotEmpty()
46+
validator.String("userId", &l.UserID).NotEmpty()
47+
validator.Time("createdAt", &l.CreatedAt).NotZero()
48+
}

notifications/logs/mongo_logs.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package logs
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"go.mongodb.org/mongo-driver/bson"
8+
"go.mongodb.org/mongo-driver/mongo"
9+
10+
"github.com/tidepool-org/platform/errors"
11+
"github.com/tidepool-org/platform/log"
12+
storeStructuredMongo "github.com/tidepool-org/platform/store/structured/mongo"
13+
structureValidator "github.com/tidepool-org/platform/structure/validator"
14+
)
15+
16+
type LogsRepository struct {
17+
*storeStructuredMongo.Repository
18+
}
19+
20+
func NewLogsRepository(store *storeStructuredMongo.Store) *LogsRepository {
21+
return &LogsRepository{
22+
Repository: store.GetRepository("notification_logs"),
23+
}
24+
}
25+
26+
func (p *LogsRepository) EnsureIndexes() error {
27+
return p.CreateAllIndexes(context.Background(), []mongo.IndexModel{
28+
{
29+
Keys: bson.D{
30+
{Key: "userId", Value: 1},
31+
{Key: "type", Value: 1},
32+
{Key: "createdAt", Value: 1},
33+
},
34+
},
35+
})
36+
}
37+
38+
func (p *LogsRepository) Add(ctx context.Context, entry LogEntry) error {
39+
if err := structureValidator.New(log.LoggerFromContext(ctx)).Validate(&entry); err != nil {
40+
return errors.Wrap(err, "filter is invalid")
41+
}
42+
43+
entry.CreatedAt = time.Now()
44+
_, err := p.InsertOne(ctx, entry)
45+
if err != nil {
46+
return errors.Wrap(err, "unable to persist notification log entry")
47+
}
48+
49+
return nil
50+
}
51+
52+
func (p *LogsRepository) List(ctx context.Context, filter Filter) ([]LogEntry, error) {
53+
selector := bson.M{}
54+
if filter.UserID != "" {
55+
selector["userId"] = filter.UserID
56+
}
57+
if filter.Type != "" {
58+
selector["type"] = filter.Type
59+
}
60+
if filter.DataSourceID != "" {
61+
selector["dataSourceId"] = filter.DataSourceID
62+
}
63+
if filter.GroupID != "" {
64+
selector["groupId"] = filter.GroupID
65+
}
66+
cursor, err := p.Find(ctx, selector)
67+
if err != nil {
68+
return nil, err
69+
}
70+
var entries []LogEntry
71+
if err := cursor.All(ctx, &entries); err != nil {
72+
return nil, err
73+
}
74+
return entries, nil
75+
}

notifications/notifications.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
dataSourceStore "github.com/tidepool-org/platform/data/source/store/structured"
1212
"github.com/tidepool-org/platform/errors"
1313
"github.com/tidepool-org/platform/mailer"
14+
"github.com/tidepool-org/platform/notifications/logs"
1415
"github.com/tidepool-org/platform/pointer"
1516
"github.com/tidepool-org/platform/user"
1617
"github.com/tidepool-org/platform/work"
@@ -26,6 +27,7 @@ type Dependencies struct {
2627
Clinics clinics.Client
2728
Confirmation confirmationClient.ClientWithResponsesInterface
2829
DataSources dataSourceStore.DataSourcesRepository
30+
Logs logs.Logger
2931
Mailer mailer.Mailer
3032
Users user.Client
3133
Worker work.Client

0 commit comments

Comments
 (0)