Skip to content

Commit 148dae1

Browse files
authored
fix: add fallback icons for notifications (#17013)
Related: coder/internal#522
1 parent ca414b0 commit 148dae1

File tree

6 files changed

+187
-4
lines changed

6 files changed

+187
-4
lines changed

coderd/inboxnotifications.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/coder/coder/v2/coderd/database/dbtime"
1717
"github.com/coder/coder/v2/coderd/httpapi"
1818
"github.com/coder/coder/v2/coderd/httpmw"
19+
"github.com/coder/coder/v2/coderd/notifications"
1920
"github.com/coder/coder/v2/coderd/pubsub"
2021
markdown "github.com/coder/coder/v2/coderd/render"
2122
"github.com/coder/coder/v2/codersdk"
@@ -28,9 +29,51 @@ const (
2829
notificationFormatPlaintext = "plaintext"
2930
)
3031

32+
var fallbackIcons = map[uuid.UUID]string{
33+
// workspace related notifications
34+
notifications.TemplateWorkspaceCreated: codersdk.FallbackIconWorkspace,
35+
notifications.TemplateWorkspaceManuallyUpdated: codersdk.FallbackIconWorkspace,
36+
notifications.TemplateWorkspaceDeleted: codersdk.FallbackIconWorkspace,
37+
notifications.TemplateWorkspaceAutobuildFailed: codersdk.FallbackIconWorkspace,
38+
notifications.TemplateWorkspaceDormant: codersdk.FallbackIconWorkspace,
39+
notifications.TemplateWorkspaceAutoUpdated: codersdk.FallbackIconWorkspace,
40+
notifications.TemplateWorkspaceMarkedForDeletion: codersdk.FallbackIconWorkspace,
41+
notifications.TemplateWorkspaceManualBuildFailed: codersdk.FallbackIconWorkspace,
42+
notifications.TemplateWorkspaceOutOfMemory: codersdk.FallbackIconWorkspace,
43+
notifications.TemplateWorkspaceOutOfDisk: codersdk.FallbackIconWorkspace,
44+
45+
// account related notifications
46+
notifications.TemplateUserAccountCreated: codersdk.FallbackIconAccount,
47+
notifications.TemplateUserAccountDeleted: codersdk.FallbackIconAccount,
48+
notifications.TemplateUserAccountSuspended: codersdk.FallbackIconAccount,
49+
notifications.TemplateUserAccountActivated: codersdk.FallbackIconAccount,
50+
notifications.TemplateYourAccountSuspended: codersdk.FallbackIconAccount,
51+
notifications.TemplateYourAccountActivated: codersdk.FallbackIconAccount,
52+
notifications.TemplateUserRequestedOneTimePasscode: codersdk.FallbackIconAccount,
53+
54+
// template related notifications
55+
notifications.TemplateTemplateDeleted: codersdk.FallbackIconTemplate,
56+
notifications.TemplateTemplateDeprecated: codersdk.FallbackIconTemplate,
57+
notifications.TemplateWorkspaceBuildsFailedReport: codersdk.FallbackIconTemplate,
58+
}
59+
60+
func ensureNotificationIcon(notif codersdk.InboxNotification) codersdk.InboxNotification {
61+
if notif.Icon != "" {
62+
return notif
63+
}
64+
65+
fallbackIcon, ok := fallbackIcons[notif.TemplateID]
66+
if !ok {
67+
fallbackIcon = codersdk.FallbackIconOther
68+
}
69+
70+
notif.Icon = fallbackIcon
71+
return notif
72+
}
73+
3174
// convertInboxNotificationResponse works as a util function to transform a database.InboxNotification to codersdk.InboxNotification
3275
func convertInboxNotificationResponse(ctx context.Context, logger slog.Logger, notif database.InboxNotification) codersdk.InboxNotification {
33-
return codersdk.InboxNotification{
76+
convertedNotif := codersdk.InboxNotification{
3477
ID: notif.ID,
3578
UserID: notif.UserID,
3679
TemplateID: notif.TemplateID,
@@ -54,6 +97,8 @@ func convertInboxNotificationResponse(ctx context.Context, logger slog.Logger, n
5497
}(),
5598
CreatedAt: notif.CreatedAt,
5699
}
100+
101+
return ensureNotificationIcon(convertedNotif)
57102
}
58103

59104
// watchInboxNotifications watches for new inbox notifications and sends them to the client.
@@ -147,7 +192,7 @@ func (api *API) watchInboxNotifications(rw http.ResponseWriter, r *http.Request)
147192

148193
// keep a safe guard in case of latency to push notifications through websocket
149194
select {
150-
case notificationCh <- payload.InboxNotification:
195+
case notificationCh <- ensureNotificationIcon(payload.InboxNotification):
151196
default:
152197
api.Logger.Error(ctx, "failed to push consumed notification into websocket handler, check latency")
153198
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package coderd
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/google/uuid"
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/coder/coder/v2/coderd/notifications"
11+
"github.com/coder/coder/v2/codersdk"
12+
)
13+
14+
func TestInboxNotifications_ensureNotificationIcon(t *testing.T) {
15+
t.Parallel()
16+
17+
tests := []struct {
18+
name string
19+
icon string
20+
templateID uuid.UUID
21+
expectedIcon string
22+
}{
23+
{"WorkspaceCreated", "", notifications.TemplateWorkspaceCreated, codersdk.FallbackIconWorkspace},
24+
{"UserAccountCreated", "", notifications.TemplateUserAccountCreated, codersdk.FallbackIconAccount},
25+
{"TemplateDeleted", "", notifications.TemplateTemplateDeleted, codersdk.FallbackIconTemplate},
26+
{"TestNotification", "", notifications.TemplateTestNotification, codersdk.FallbackIconOther},
27+
{"TestExistingIcon", "https://cdn.coder.com/icon_notif.png", notifications.TemplateTemplateDeleted, "https://cdn.coder.com/icon_notif.png"},
28+
{"UnknownTemplate", "", uuid.New(), codersdk.FallbackIconOther},
29+
}
30+
31+
for _, tt := range tests {
32+
tt := tt
33+
34+
t.Run(tt.name, func(t *testing.T) {
35+
t.Parallel()
36+
37+
notif := codersdk.InboxNotification{
38+
ID: uuid.New(),
39+
UserID: uuid.New(),
40+
TemplateID: tt.templateID,
41+
Title: "notification title",
42+
Content: "notification content",
43+
Icon: tt.icon,
44+
CreatedAt: time.Now(),
45+
}
46+
47+
notif = ensureNotificationIcon(notif)
48+
require.Equal(t, tt.expectedIcon, notif.Icon)
49+
})
50+
}
51+
}

coderd/inboxnotifications_test.go

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ func TestInboxNotification_Watch(t *testing.T) {
135135

136136
require.Equal(t, 1, notif.UnreadCount)
137137
require.Equal(t, memberClient.ID, notif.Notification.UserID)
138+
139+
// check for the fallback icon logic
140+
require.Equal(t, codersdk.FallbackIconWorkspace, notif.Notification.Icon)
138141
})
139142

140143
t.Run("OK - change format", func(t *testing.T) {
@@ -474,8 +477,9 @@ func TestInboxNotifications_List(t *testing.T) {
474477
TemplateID: notifications.TemplateWorkspaceOutOfMemory,
475478
Title: fmt.Sprintf("Notification %d", i),
476479
Actions: json.RawMessage("[]"),
477-
Content: fmt.Sprintf("Content of the notif %d", i),
478-
CreatedAt: dbtime.Now(),
480+
481+
Content: fmt.Sprintf("Content of the notif %d", i),
482+
CreatedAt: dbtime.Now(),
479483
})
480484
}
481485

@@ -498,6 +502,68 @@ func TestInboxNotifications_List(t *testing.T) {
498502
require.Equal(t, "Notification 14", notifs.Notifications[0].Title)
499503
})
500504

505+
t.Run("OK check icons", func(t *testing.T) {
506+
t.Parallel()
507+
508+
client, _, api := coderdtest.NewWithAPI(t, &coderdtest.Options{})
509+
firstUser := coderdtest.CreateFirstUser(t, client)
510+
client, member := coderdtest.CreateAnotherUser(t, client, firstUser.OrganizationID)
511+
512+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
513+
defer cancel()
514+
515+
notifs, err := client.ListInboxNotifications(ctx, codersdk.ListInboxNotificationsRequest{})
516+
require.NoError(t, err)
517+
require.NotNil(t, notifs)
518+
require.Equal(t, 0, notifs.UnreadCount)
519+
require.Empty(t, notifs.Notifications)
520+
521+
for i := range 10 {
522+
dbgen.NotificationInbox(t, api.Database, database.InsertInboxNotificationParams{
523+
ID: uuid.New(),
524+
UserID: member.ID,
525+
TemplateID: func() uuid.UUID {
526+
switch i {
527+
case 0:
528+
return notifications.TemplateWorkspaceCreated
529+
case 1:
530+
return notifications.TemplateWorkspaceMarkedForDeletion
531+
case 2:
532+
return notifications.TemplateUserAccountActivated
533+
case 3:
534+
return notifications.TemplateTemplateDeprecated
535+
default:
536+
return notifications.TemplateTestNotification
537+
}
538+
}(),
539+
Title: fmt.Sprintf("Notification %d", i),
540+
Actions: json.RawMessage("[]"),
541+
Icon: func() string {
542+
if i == 9 {
543+
return "https://dev.coder.com/icon.png"
544+
}
545+
546+
return ""
547+
}(),
548+
Content: fmt.Sprintf("Content of the notif %d", i),
549+
CreatedAt: dbtime.Now(),
550+
})
551+
}
552+
553+
notifs, err = client.ListInboxNotifications(ctx, codersdk.ListInboxNotificationsRequest{})
554+
require.NoError(t, err)
555+
require.NotNil(t, notifs)
556+
require.Equal(t, 10, notifs.UnreadCount)
557+
require.Len(t, notifs.Notifications, 10)
558+
559+
require.Equal(t, "https://dev.coder.com/icon.png", notifs.Notifications[0].Icon)
560+
require.Equal(t, codersdk.FallbackIconWorkspace, notifs.Notifications[9].Icon)
561+
require.Equal(t, codersdk.FallbackIconWorkspace, notifs.Notifications[8].Icon)
562+
require.Equal(t, codersdk.FallbackIconAccount, notifs.Notifications[7].Icon)
563+
require.Equal(t, codersdk.FallbackIconTemplate, notifs.Notifications[6].Icon)
564+
require.Equal(t, codersdk.FallbackIconOther, notifs.Notifications[4].Icon)
565+
})
566+
501567
t.Run("OK with template filter", func(t *testing.T) {
502568
t.Parallel()
503569

@@ -541,6 +607,7 @@ func TestInboxNotifications_List(t *testing.T) {
541607
require.Len(t, notifs.Notifications, 5)
542608

543609
require.Equal(t, "Notification 8", notifs.Notifications[0].Title)
610+
require.Equal(t, codersdk.FallbackIconWorkspace, notifs.Notifications[0].Icon)
544611
})
545612

546613
t.Run("OK with target filter", func(t *testing.T) {

coderd/notifications/events.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import "github.com/google/uuid"
44

55
// These vars are mapped to UUIDs in the notification_templates table.
66
// TODO: autogenerate these: https://github.com/coder/team-coconut/issues/36
7+
// TODO(defelmnq): add fallback icon to coderd/inboxnofication.go when adding a new template
78

89
// Workspace-related events.
910
var (

codersdk/inboxnotification.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ import (
1010
"github.com/google/uuid"
1111
)
1212

13+
const (
14+
FallbackIconWorkspace = "DEFAULT_ICON_WORKSPACE"
15+
FallbackIconAccount = "DEFAULT_ICON_ACCOUNT"
16+
FallbackIconTemplate = "DEFAULT_ICON_TEMPLATE"
17+
FallbackIconOther = "DEFAULT_ICON_OTHER"
18+
)
19+
1320
type InboxNotification struct {
1421
ID uuid.UUID `json:"id" format:"uuid"`
1522
UserID uuid.UUID `json:"user_id" format:"uuid"`

site/src/api/typesGenerated.ts

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

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy