Skip to content

Commit 30560e1

Browse files
committed
feat: don't sent the same notif more then once (#293)
1 parent 2a4e705 commit 30560e1

File tree

5 files changed

+117
-37
lines changed

5 files changed

+117
-37
lines changed

internal/app/characterservice/communications.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func (s *CharacterService) NotifyCommunications(ctx context.Context, characterID
4747
}
4848
title, content := s.RenderNotificationSummary(n)
4949
notify(title, content)
50-
if err := s.st.UpdateCharacterNotificationSetProcessed(ctx, n.ID); err != nil {
50+
if err := s.st.UpdateCharacterNotificationsSetProcessed(ctx, n.NotificationID); err != nil {
5151
return nil, err
5252
}
5353
}

internal/app/storage/characternotification.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,9 @@ func (st *Storage) ListCharacterNotificationsUnread(ctx context.Context, charact
496496
return ee, nil
497497
}
498498

499+
// ListCharacterNotificationsUnprocessed returns all unprocessed notifications for character characterID.
500+
// Notifications older then earliest are ignored or which have no body or title are ignored.
501+
// Notifications which are duplicates of already processed ones are ignored too.
499502
func (st *Storage) ListCharacterNotificationsUnprocessed(ctx context.Context, characterID int32, earliest time.Time) ([]*app.CharacterNotification, error) {
500503
arg := queries.ListCharacterNotificationsUnprocessedParams{
501504
CharacterID: int64(characterID),
@@ -573,9 +576,10 @@ func (st *Storage) UpdateCharacterNotification(ctx context.Context, arg UpdateCh
573576
return nil
574577
}
575578

576-
func (st *Storage) UpdateCharacterNotificationSetProcessed(ctx context.Context, id int64) error {
577-
if err := st.qRW.UpdateCharacterNotificationSetProcessed(ctx, id); err != nil {
578-
return fmt.Errorf("update notification set processed for id %d: %w", id, err)
579+
// UpdateCharacterNotificationsSetProcessed marks all notifications with the same notificationID as processed.
580+
func (st *Storage) UpdateCharacterNotificationsSetProcessed(ctx context.Context, notificationID int64) error {
581+
if err := st.qRW.UpdateCharacterNotificationsSetProcessed(ctx, notificationID); err != nil {
582+
return fmt.Errorf("UpdateCharacterNotificationsSetProcessed for notification ID %d: %w", notificationID, err)
579583
}
580584
return nil
581585
}

internal/app/storage/characternotification_test.go

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,20 +188,37 @@ func TestCharacterNotification(t *testing.T) {
188188
assert.Equal(t, "body", o.Body.ValueOrZero())
189189
}
190190
})
191-
t.Run("can set processed", func(t *testing.T) {
191+
t.Run("can mark notifs as processed", func(t *testing.T) {
192192
// given
193193
testutil.TruncateTables(db)
194-
n := factory.CreateCharacterNotification(storage.CreateCharacterNotificationParams{})
194+
c1 := factory.CreateCharacter()
195+
n1 := factory.CreateCharacterNotification(storage.CreateCharacterNotificationParams{
196+
CharacterID: c1.ID,
197+
Body: optional.New("Body"),
198+
Title: optional.New("Title"),
199+
})
200+
factory.CreateCharacterNotification(storage.CreateCharacterNotificationParams{
201+
CharacterID: c1.ID,
202+
NotificationID: 42,
203+
})
204+
factory.CreateCharacterNotification(storage.CreateCharacterNotificationParams{
205+
NotificationID: 42,
206+
})
195207
// when
196-
err := st.UpdateCharacterNotificationSetProcessed(ctx, n.ID)
208+
err := st.UpdateCharacterNotificationsSetProcessed(ctx, 42)
197209
// then
198210
if !assert.NoError(t, err) {
199211
t.Fatal(err)
200212
}
201-
o, err := st.GetCharacterNotification(ctx, n.CharacterID, n.ID)
202-
if assert.NoError(t, err) {
203-
assert.True(t, o.IsProcessed)
213+
ee, err := st.ListCharacterNotificationsUnprocessed(ctx, c1.ID, time.Now().Add(-24*time.Hour))
214+
if !assert.NoError(t, err) {
215+
t.Fatal(err)
204216
}
217+
got := set.Collect(xiter.MapSlice(ee, func(x *app.CharacterNotification) int64 {
218+
return x.ID
219+
}))
220+
want := set.Of(n1.ID)
221+
assert.True(t, got.Equal(want), "got %q, wanted %q", got, want)
205222
})
206223
t.Run("can calculate counts", func(t *testing.T) {
207224
// given
@@ -417,6 +434,46 @@ func TestCharacterNotification_ListUnprocessed(t *testing.T) {
417434
want := set.Of(n1.ID)
418435
assert.True(t, got.Equal(want), "got %q, wanted %q", got, want)
419436
})
437+
t.Run("should not return duplicates of processed notifs", func(t *testing.T) {
438+
// given
439+
testutil.TruncateTables(db)
440+
c := factory.CreateCharacter()
441+
now := time.Now().UTC()
442+
n1 := factory.CreateCharacterNotification(storage.CreateCharacterNotificationParams{
443+
Body: optional.New("body"),
444+
CharacterID: c.ID,
445+
Type: "bravo",
446+
Timestamp: now,
447+
Title: optional.New("title"),
448+
})
449+
factory.CreateCharacterNotification(storage.CreateCharacterNotificationParams{
450+
Body: optional.New("body"),
451+
CharacterID: c.ID,
452+
NotificationID: 42,
453+
Type: "bravo",
454+
Timestamp: now,
455+
Title: optional.New("title"),
456+
})
457+
factory.CreateCharacterNotification(storage.CreateCharacterNotificationParams{
458+
Body: optional.New("body"),
459+
NotificationID: 42,
460+
IsProcessed: true,
461+
Type: "bravo",
462+
Timestamp: now,
463+
Title: optional.New("title"),
464+
})
465+
// when
466+
ee, err := st.ListCharacterNotificationsUnprocessed(ctx, c.ID, now.Add(-24*time.Hour))
467+
// then
468+
if !assert.NoError(t, err) {
469+
t.Fatal(err)
470+
}
471+
got := set.Collect(xiter.MapSlice(ee, func(x *app.CharacterNotification) int64 {
472+
return x.ID
473+
}))
474+
want := set.Of(n1.ID)
475+
assert.True(t, got.Equal(want), "got %q, wanted %q", got, want)
476+
})
420477
}
421478

422479
func TestNotificationType(t *testing.T) {

internal/app/storage/queries/character_notifications.sql

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ INSERT INTO
2323
notification_id,
2424
recipient_id,
2525
sender_id,
26-
text,
26+
TEXT,
2727
timestamp,
2828
title,
2929
type_id
@@ -121,37 +121,46 @@ FROM
121121
JOIN notification_types nt ON nt.id = cn.type_id
122122
LEFT JOIN eve_entities recipient ON recipient.id = cn.recipient_id
123123
WHERE
124-
character_id = ?
124+
cn.character_id = ?
125125
AND cn.is_processed IS FALSE
126-
AND title IS NOT NULL
127-
AND body IS NOT NULL
128-
AND timestamp > ?
126+
AND cn.title IS NOT NULL
127+
AND cn.body IS NOT NULL
128+
AND cn.timestamp > ?
129+
AND notification_id NOT IN (
130+
SELECT
131+
cn2.notification_id
132+
FROM
133+
character_notifications cn2
134+
WHERE
135+
cn2.is_processed IS TRUE
136+
AND cn2.timestamp > ?
137+
)
129138
ORDER BY
130139
timestamp;
131140

132141
-- name: UpdateCharacterNotification :exec
133-
UPDATE
134-
character_notifications
142+
UPDATE character_notifications
135143
SET
136144
body = ?2,
137145
is_read = ?3,
138146
title = ?4
139147
WHERE
140148
id = ?1;
141149

142-
-- name: UpdateCharacterNotificationSetProcessed :exec
143-
UPDATE
144-
character_notifications
150+
-- name: UpdateCharacterNotificationsSetProcessed :exec
151+
UPDATE character_notifications
145152
SET
146153
is_processed = TRUE
147154
WHERE
148-
id = ?1;
155+
notification_id = ?;
149156

150157
-- name: CreateNotificationType :one
151158
INSERT INTO
152159
notification_types (name)
153160
VALUES
154-
(?) RETURNING id;
161+
(?)
162+
RETURNING
163+
id;
155164

156165
-- name: GetNotificationTypeID :one
157166
SELECT

internal/app/storage/queries/character_notifications.sql.go

Lines changed: 25 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)